From eb5dd4f569bbbea709e6da5a10a4337948aa5f3c Mon Sep 17 00:00:00 2001 From: Chris Price Date: Mon, 18 Mar 2024 09:32:59 -0700 Subject: [PATCH 1/4] feat: add get/set to CacheClient to support readme.ts example The main goal of this PR is to get us closer to having a good example code snippet in the readme.ts file. There were a few things I discovered that we need along the way toward that, so this PR tries to bite off a few of them: * We need `get` and `set`, so those are added in this PR. * The credential provider builder pattern didn't seem quite as ergonomic as some of our newer ones (CacheClient/requests), and wasn't consistent with the direction of them, so I tweaked a few things to try to move it in that direction. This is not the final state of this, I intend to do at least one more pass to try to make sure that the builders/constructors for client, requests, cred providers, and configs are as consistent as possible, but this is an interim step. * I also removed/commented out the support for overriding endpoints in the credential providers for now; these had gotten a little stale relative to the current endpoints support, and I'd rather keep the API surface area as small as possible for now. We will probably have to add these back in in some fashion in order to update the CLI to the new release of the SDK but we can do that when we get there. I will also add a copy paste of what the example code will look like after this PR, but it can't be merged until we do a release because it exists in the examples dir and must be pinned to a released version of the SDK. --- src/cache_client.rs | 257 ++++++++-- src/credential_provider.rs | 442 +++++++++--------- src/lib.rs | 2 +- src/preview/topics.rs | 5 +- src/requests/cache/basic/get.rs | 169 +++++++ src/requests/cache/basic/mod.rs | 2 + src/requests/cache/basic/set.rs | 68 +++ src/requests/cache/mod.rs | 8 +- src/requests/cache/set/mod.rs | 1 + .../cache/{ => set}/set_add_elements.rs | 7 +- src/requests/cache/sorted_set/mod.rs | 4 + .../sorted_set_fetch_by_rank.rs | 7 +- .../sorted_set_fetch_by_score.rs | 8 +- .../sorted_set_put_element.rs | 4 +- .../sorted_set_put_elements.rs | 4 +- src/response/cache/sorted_set_fetch.rs | 2 + src/simple_cache_client.rs | 20 +- src/utils.rs | 15 +- test-util/src/lib.rs | 11 +- tests/cache_sorted_set_tests.rs | 8 +- tests/integration_test.rs | 6 +- 21 files changed, 735 insertions(+), 315 deletions(-) create mode 100644 src/requests/cache/basic/get.rs create mode 100644 src/requests/cache/basic/mod.rs create mode 100644 src/requests/cache/basic/set.rs create mode 100644 src/requests/cache/set/mod.rs rename src/requests/cache/{ => set}/set_add_elements.rs (90%) create mode 100644 src/requests/cache/sorted_set/mod.rs rename src/requests/cache/{ => sorted_set}/sorted_set_fetch_by_rank.rs (94%) rename src/requests/cache/{ => sorted_set}/sorted_set_fetch_by_score.rs (94%) rename src/requests/cache/{ => sorted_set}/sorted_set_put_element.rs (94%) rename src/requests/cache/{ => sorted_set}/sorted_set_put_elements.rs (94%) diff --git a/src/cache_client.rs b/src/cache_client.rs index 989b4e25..af769eb1 100644 --- a/src/cache_client.rs +++ b/src/cache_client.rs @@ -8,15 +8,19 @@ use tonic::transport::Channel; use crate::config::configuration::Configuration; use crate::grpc::header_interceptor::HeaderInterceptor; +use crate::requests::cache::basic::get::{Get, GetRequest}; +use crate::requests::cache::basic::set::{Set, SetRequest}; use crate::requests::cache::create_cache::{CreateCache, CreateCacheRequest}; use crate::requests::cache::delete_cache::{DeleteCache, DeleteCacheRequest}; -use crate::requests::cache::set_add_elements::{SetAddElements, SetAddElementsRequest}; -use crate::requests::cache::sorted_set_fetch_by_rank::{SortOrder, SortedSetFetchByRankRequest}; -use crate::requests::cache::sorted_set_fetch_by_score::SortedSetFetchByScoreRequest; -use crate::requests::cache::sorted_set_put_element::{ +use crate::requests::cache::set::set_add_elements::{SetAddElements, SetAddElementsRequest}; +use crate::requests::cache::sorted_set::sorted_set_fetch_by_rank::{ + SortOrder, SortedSetFetchByRankRequest, +}; +use crate::requests::cache::sorted_set::sorted_set_fetch_by_score::SortedSetFetchByScoreRequest; +use crate::requests::cache::sorted_set::sorted_set_put_element::{ SortedSetPutElement, SortedSetPutElementRequest, }; -use crate::requests::cache::sorted_set_put_elements::{ +use crate::requests::cache::sorted_set::sorted_set_put_elements::{ SortedSetPutElements, SortedSetPutElementsRequest, }; use crate::requests::cache::MomentoRequest; @@ -161,31 +165,182 @@ impl CacheClient { request.send(self).await } + /// Sets an item in a Momento Cache + /// + /// # Arguments + /// + /// * `cache_name` - name of cache + /// * `key` - key of the item whose value we are setting + /// * `value` - data to stored in the cache item + /// * `ttl` - The TTL to use for the + /// + /// # Examples + /// + /// ``` + /// # fn main() -> anyhow::Result<()> { + /// # use momento_test_util::create_doctest_client; + /// # tokio_test::block_on(async { + /// # let (cache_client, cache_name) = create_doctest_client(); + /// + /// cache_client.set(&cache_name, "k1", "v1").await?; + /// + /// # Ok(()) + /// # }) + /// # } + /// ``` + /// You can also use the [send_request](CacheClient::send_request) method to set an item using a [SetRequest]: /// ``` /// # fn main() -> anyhow::Result<()> { + /// use momento_test_util::create_doctest_client; /// # tokio_test::block_on(async { /// use std::time::Duration; - /// use momento::config::configurations; - /// use momento::CredentialProviderBuilder; - /// use momento::requests::cache::set_add_elements::SetAddElements; + /// use momento::requests::cache::basic::set::Set; + /// use momento::requests::cache::basic::set::SetRequest; + /// # let (cache_client, cache_name) = create_doctest_client(); /// - /// let credential_provider = CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string()) - /// .build()?; - /// let cache_name = "cache"; + /// let set_request = SetRequest::new( + /// cache_name.to_string(), + /// "key", + /// "value1" + /// ).with_ttl(Duration::from_secs(60)); /// - /// let cache_client = momento::CacheClient::new( - /// credential_provider, - /// configurations::laptop::latest(), - /// Duration::from_secs(5), - ///)?; + /// let set_response = cache_client.send_request(set_request).await?; /// - /// let set_add_elements_response = cache_client.set_add_elements(cache_name.to_string(), "set", vec!["element1", "element2"]).await?; - /// assert_eq!(set_add_elements_response, SetAddElements {}); + /// assert_eq!(set_response, Set {}); /// # Ok(()) /// # }) - /// # - /// } + /// # } + pub async fn set( + &self, + cache_name: &str, + key: impl IntoBytes, + value: impl IntoBytes, + ) -> MomentoResult { + let request = SetRequest::new(cache_name.to_string(), key, value); + request.send(self).await + } + + /// Gets an item from a Momento Cache + /// + /// # Arguments + /// + /// * `cache_name` - name of cache + /// * `key` - key of entry within the cache. + /// + /// # Examples + /// + /// ``` + /// # fn main() -> anyhow::Result<()> { + /// # use momento_test_util::create_doctest_client; + /// # tokio_test::block_on(async { + /// # let (cache_client, cache_name) = create_doctest_client(); + /// use std::convert::TryInto; + /// use momento::requests::cache::basic::get::Get; + /// + /// cache_client.set(&cache_name, "key", "value").await?; + /// + /// let item: String = match(cache_client.get(&cache_name, "key").await?) { + /// Get::Hit { value } => value.try_into().expect("I stored a string!"), + /// Get::Miss => return Err(anyhow::Error::msg("cache miss")) // probably you'll do something else here + /// }; + /// + /// # assert_eq!(item, "value"); + /// + /// # Ok(()) + /// # }) + /// # } + /// ``` + /// You can also use the [send_request](CacheClient::send_request) method to get an item using a [GetRequest]: + /// ``` + /// # fn main() -> anyhow::Result<()> { + /// use momento_test_util::create_doctest_client; + /// # tokio_test::block_on(async { + /// use std::time::Duration; + /// use std::convert::TryInto; + /// use momento::requests::cache::basic::get::Get; + /// use momento::requests::cache::basic::get::GetRequest; + /// # let (cache_client, cache_name) = create_doctest_client(); + /// + /// cache_client.set(&cache_name, "key", "value").await?; + /// + /// let get_request = GetRequest::new( + /// cache_name, + /// "key" + /// ); + /// + /// let item: String = match(cache_client.send_request(get_request).await?) { + /// Get::Hit { value } => value.try_into().expect("I stored a string!"), + /// Get::Miss => return Err(anyhow::Error::msg("cache miss")) // probably you'll do something else here + /// }; + /// + /// # assert_eq!(item, "value"); + /// + /// # Ok(()) + /// # }) + /// # } + pub async fn get(&self, cache_name: &str, key: impl IntoBytes) -> MomentoResult { + let request = GetRequest::new(cache_name.to_string(), key); + request.send(self).await + } + + /// Adds elements to the given set. Creates the set if it does not exist. + /// + /// # Arguments + /// + /// * `cache_name` - The name of the cache containing the sorted set. + /// * `set_name` - The name of the sorted set to add an element to. + /// * `elements` - The elements to add. Must be able to be converted to a Vec. + /// + /// # Optional Arguments + /// If you use [send_request](CacheClient::send_request) to add an element using a + /// [SetAddElementsRequest], you can also provide the following optional arguments: + /// + /// * `collection_ttl` - The time-to-live for the collection. If not provided, the client's default time-to-live is used. + /// + /// # Examples + /// Assumes that a CacheClient named `cache_client` has been created and is available. /// ``` + /// # fn main() -> anyhow::Result<()> { + /// # use momento_test_util::create_doctest_client; + /// # tokio_test::block_on(async { + /// use momento::requests::cache::set::set_add_elements::SetAddElements; + /// # let (cache_client, cache_name) = create_doctest_client(); + /// let set_name = "set"; + /// + /// let add_elements_response = cache_client.set_add_elements( + /// cache_name.to_string(), + /// set_name.to_string(), + /// vec!["value1", "value2"] + /// ).await?; + /// + /// assert_eq!(add_elements_response, SetAddElements {}); + /// # Ok(()) + /// # }) + /// # } + /// ``` + /// You can also use the [send_request](CacheClient::send_request) method to create a cache using a [SetAddElementsRequest]: + /// ``` + /// # fn main() -> anyhow::Result<()> { + /// # use momento_test_util::create_doctest_client; + /// # tokio_test::block_on(async { + /// use momento::CollectionTtl; + /// use momento::requests::cache::set::set_add_elements::SetAddElements; + /// use momento::requests::cache::set::set_add_elements::SetAddElementsRequest; + /// # let (cache_client, cache_name) = create_doctest_client(); + /// let set_name = "set"; + /// + /// let add_elements_request = SetAddElementsRequest::new( + /// cache_name.to_string(), + /// set_name.to_string(), + /// vec!["value1", "value2"] + /// ).with_ttl(CollectionTtl::default()); + /// + /// let add_elements_response = cache_client.send_request(add_elements_request).await?; + /// + /// assert_eq!(add_elements_response, SetAddElements {}); + /// # Ok(()) + /// # }) + /// # } pub async fn set_add_elements( &self, cache_name: String, @@ -218,7 +373,7 @@ impl CacheClient { /// # fn main() -> anyhow::Result<()> { /// # use momento_test_util::create_doctest_client; /// # tokio_test::block_on(async { - /// use momento::requests::cache::sorted_set_put_element::SortedSetPutElement; + /// use momento::requests::cache::sorted_set::sorted_set_put_element::SortedSetPutElement; /// # let (cache_client, cache_name) = create_doctest_client(); /// let sorted_set_name = "sorted_set"; /// @@ -240,8 +395,8 @@ impl CacheClient { /// # use momento_test_util::create_doctest_client; /// # tokio_test::block_on(async { /// use momento::CollectionTtl; - /// use momento::requests::cache::sorted_set_put_element::SortedSetPutElement; - /// use momento::requests::cache::sorted_set_put_element::SortedSetPutElementRequest; + /// use momento::requests::cache::sorted_set::sorted_set_put_element::SortedSetPutElement; + /// use momento::requests::cache::sorted_set::sorted_set_put_element::SortedSetPutElementRequest; /// # let (cache_client, cache_name) = create_doctest_client(); /// let sorted_set_name = "sorted_set"; /// @@ -258,11 +413,11 @@ impl CacheClient { /// # Ok(()) /// # }) /// # } - pub async fn sorted_set_put_element( + pub async fn sorted_set_put_element( &self, cache_name: String, sorted_set_name: impl IntoBytes, - value: E, + value: impl IntoBytes, score: f64, ) -> MomentoResult { let request = SortedSetPutElementRequest::new(cache_name, sorted_set_name, value, score); @@ -290,7 +445,7 @@ impl CacheClient { /// # fn main() -> anyhow::Result<()> { /// # use momento_test_util::create_doctest_client; /// # tokio_test::block_on(async { - /// use momento::requests::cache::sorted_set_put_elements::SortedSetPutElements; + /// use momento::requests::cache::sorted_set::sorted_set_put_elements::SortedSetPutElements; /// # let (cache_client, cache_name) = create_doctest_client(); /// let sorted_set_name = "sorted_set"; /// @@ -311,8 +466,8 @@ impl CacheClient { /// # use momento_test_util::create_doctest_client; /// # tokio_test::block_on(async { /// use momento::CollectionTtl; - /// use momento::requests::cache::sorted_set_put_elements::SortedSetPutElements; - /// use momento::requests::cache::sorted_set_put_elements::SortedSetPutElementsRequest; + /// use momento::requests::cache::sorted_set::sorted_set_put_elements::SortedSetPutElements; + /// use momento::requests::cache::sorted_set::sorted_set_put_elements::SortedSetPutElementsRequest; /// # let (cache_client, cache_name) = create_doctest_client(); /// let sorted_set_name = "sorted_set"; /// @@ -363,7 +518,7 @@ impl CacheClient { /// # use momento::MomentoResult; /// # use momento_test_util::create_doctest_client; /// # tokio_test::block_on(async { - /// use momento::requests::cache::sorted_set_fetch_by_rank::SortOrder; + /// use momento::requests::cache::sorted_set::sorted_set_fetch_by_rank::SortOrder; /// use momento::response::cache::sorted_set_fetch::SortedSetFetch; /// # let (cache_client, cache_name) = create_doctest_client(); /// let sorted_set_name = "sorted_set"; @@ -397,8 +552,8 @@ impl CacheClient { /// # use std::convert::TryInto; /// # use momento_test_util::create_doctest_client; /// # tokio_test::block_on(async { - /// use momento::requests::cache::sorted_set_fetch_by_rank::SortOrder; - /// use momento::requests::cache::sorted_set_fetch_by_rank::SortedSetFetchByRankRequest; + /// use momento::requests::cache::sorted_set::sorted_set_fetch_by_rank::SortOrder; + /// use momento::requests::cache::sorted_set::sorted_set_fetch_by_rank::SortedSetFetchByRankRequest; /// use momento::response::cache::sorted_set_fetch::SortedSetFetch; /// # let (cache_client, cache_name) = create_doctest_client(); /// let sorted_set_name = "sorted_set"; @@ -422,10 +577,10 @@ impl CacheClient { /// # Ok(()) /// # }) /// # } - pub async fn sorted_set_fetch_by_rank( + pub async fn sorted_set_fetch_by_rank( &self, cache_name: String, - sorted_set_name: S, + sorted_set_name: impl IntoBytes, order: SortOrder, ) -> MomentoResult { let request = @@ -461,7 +616,7 @@ impl CacheClient { /// # use momento::MomentoResult; /// # use momento_test_util::create_doctest_client; /// # tokio_test::block_on(async { - /// use momento::requests::cache::sorted_set_fetch_by_rank::SortOrder; + /// use momento::requests::cache::sorted_set::sorted_set_fetch_by_rank::SortOrder; /// use momento::response::cache::sorted_set_fetch::SortedSetFetch; /// # let (cache_client, cache_name) = create_doctest_client(); /// let sorted_set_name = "sorted_set"; @@ -495,8 +650,8 @@ impl CacheClient { /// # use std::convert::TryInto; /// # use momento_test_util::create_doctest_client; /// # tokio_test::block_on(async { - /// use momento::requests::cache::sorted_set_fetch_by_rank::SortOrder; - /// use momento::requests::cache::sorted_set_fetch_by_score::SortedSetFetchByScoreRequest; + /// use momento::requests::cache::sorted_set::sorted_set_fetch_by_rank::SortOrder; + /// use momento::requests::cache::sorted_set::sorted_set_fetch_by_score::SortedSetFetchByScoreRequest; /// use momento::response::cache::sorted_set_fetch::SortedSetFetch; /// # let (cache_client, cache_name) = create_doctest_client(); /// let sorted_set_name = "sorted_set"; @@ -520,10 +675,10 @@ impl CacheClient { /// # Ok(()) /// # }) /// # } - pub async fn sorted_set_fetch_by_score( + pub async fn sorted_set_fetch_by_score( &self, cache_name: String, - sorted_set_name: S, + sorted_set_name: impl IntoBytes, order: SortOrder, ) -> MomentoResult { let request = @@ -531,19 +686,24 @@ impl CacheClient { request.send(self).await } + /// Lower-level API to send any type of MomentoRequest to the server. This is used for cases when + /// you want to set optional fields on a request that are not supported by the short-hand API for + /// that request type. + /// /// ``` /// # fn main() -> anyhow::Result<()> { /// # use momento_protos::cache_client::update_ttl_response::Result::Set; - /// use momento::requests::cache::set_add_elements::SetAddElementsRequest; /// tokio_test::block_on(async { /// use std::time::Duration; + /// use momento::CredentialProvider; /// use momento::config::configurations; - /// use momento::CredentialProviderBuilder; - /// use momento::requests::cache::set_add_elements::SetAddElements; + /// use momento::requests::cache::sorted_set::sorted_set_fetch_by_rank::SortedSetFetchByRankRequest; + /// use momento::requests::cache::sorted_set::sorted_set_fetch_by_rank::SortOrder; + /// use momento::response::cache::sorted_set_fetch::SortedSetFetch; /// - /// let credential_provider = CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string()) - /// .build()?; + /// let credential_provider = CredentialProvider::from_env_var("MOMENTO_API_KEY".to_string())?; /// let cache_name = "cache"; + /// let sorted_set_name = "sorted_set"; /// /// let cache_client = momento::CacheClient::new( /// credential_provider, @@ -551,10 +711,13 @@ impl CacheClient { /// Duration::from_secs(5), ///)?; /// - /// let set_add_elements_response = cache_client.send_request( - /// SetAddElementsRequest::new(cache_name.to_string(), "set", vec!["element1", "element2"]) - /// ).await?; - /// assert_eq!(set_add_elements_response, SetAddElements {}); + /// let fetch_request = SortedSetFetchByRankRequest::new(cache_name.to_string(), sorted_set_name) + /// .with_order(SortOrder::Ascending) + /// .with_start_rank(1) + /// .with_end_rank(3); + /// + /// let fetch_response = cache_client.send_request(fetch_request).await?; + /// assert_eq!(fetch_response, SortedSetFetch::Miss {}); /// # Ok(()) /// # }) /// # diff --git a/src/credential_provider.rs b/src/credential_provider.rs index 4e8d7939..4d177330 100644 --- a/src/credential_provider.rs +++ b/src/credential_provider.rs @@ -31,6 +31,59 @@ pub struct CredentialProvider { pub cache_endpoint: String, } +impl CredentialProvider { + /// Returns a builder to produce a Credential Provider using an API key stored in the specified + /// environment variable + /// + /// # Arguments + /// + /// * `env_var_name` - Name of the environment variable to read token from + /// # Examples + /// + /// ``` + /// # tokio_test::block_on(async { + /// use momento::CredentialProvider; + /// let credential_provider = CredentialProvider::from_env_var("MOMENTO_API_KEY".to_string()) + /// .expect("MOMENTO_API_KEY must be set"); + /// # }) + /// ``` + /// + pub fn from_env_var(env_var_name: String) -> MomentoResult { + CredentialProviderBuilder::from_environment_variable(env_var_name).build() + } + + /// Returns a builder to produce a Credential Provider from the provided API key + /// + /// # Arguments + /// + /// * `api_key` - Momento API key + /// # Examples + /// + /// ``` + /// # use momento::MomentoResult; + /// # fn main() -> () { + /// # tokio_test::block_on(async { + /// use momento::CredentialProvider; + /// + /// let api_key = "YOUR API KEY GOES HERE"; + /// let credential_provider = match(CredentialProvider::from_string(api_key.to_string())) { + /// Ok(credential_provider) => credential_provider, + /// Err(e) => { + /// println!("Error while creating credential provider: {}", e); + /// return // probably you will do something else here + /// } + /// }; + /// + /// # () + /// # }) + /// # + /// } + /// ``` + pub fn from_string(auth_token: String) -> MomentoResult { + CredentialProviderBuilder::from_string(auth_token).build() + } +} + impl Debug for CredentialProvider { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("CredentialProvider") @@ -58,31 +111,14 @@ impl Debug for AuthTokenSource { } #[derive(Debug)] -pub struct CredentialProviderBuilder { +struct CredentialProviderBuilder { auth_token_source: AuthTokenSource, cache_endpoint_override: Option, control_endpoint_override: Option, } impl CredentialProviderBuilder { - /// Returns a builder to produce a Credential Provider using an auth token stored in the provided - /// environment variable - /// - /// # Arguments - /// - /// * `env_var_name` - Name of the environment variable to read token from - /// # Examples - /// - /// ``` - /// # tokio_test::block_on(async { - /// use momento::CredentialProviderBuilder; - /// let credential_provider = CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string()) - /// .build() - /// .expect("MOMENTO_API_KEY must be set"); - /// # }) - /// ``` - /// - pub fn from_environment_variable(env_var_name: String) -> Self { + fn from_environment_variable(env_var_name: String) -> Self { CredentialProviderBuilder { auth_token_source: EnvironmentVariable(env_var_name), cache_endpoint_override: None, @@ -90,22 +126,7 @@ impl CredentialProviderBuilder { } } - /// Returns a builder to produce a Credential Provider from the provided auth token - /// - /// # Arguments - /// - /// * `auth_token` - Momento auth token string - /// # Examples - /// - /// ``` - /// # tokio_test::block_on(async { - /// use momento::CredentialProviderBuilder; - /// let credential_provider = CredentialProviderBuilder::from_string("eyJlbmRwb2ludCI6Im1vbWVudG9fZW5kcG9pbnQiLCJhcGlfa2V5IjoiZXlKaGJHY2lPaUpJVXpJMU5pSjkuZXlKemRXSWlPaUowWlhOMElITjFZbXBsWTNRaUxDSjJaWElpT2pFc0luQWlPaUlpZlEuaGcyd01iV2Utd2VzUVZ0QTd3dUpjUlVMalJwaFhMUXdRVFZZZlFMM0w3YyJ9Cg==".to_string()) - /// .build() - /// .expect("could not build credential provider"); - /// # }) - /// ``` - pub fn from_string(auth_token: String) -> Self { + fn from_string(auth_token: String) -> Self { CredentialProviderBuilder { auth_token_source: LiteralToken(auth_token), cache_endpoint_override: None, @@ -113,79 +134,79 @@ impl CredentialProviderBuilder { } } - /// Override the data plane endpoint - /// # Arguments - /// - /// * `cache_endpoint_override` - The host which the Momento client will connect to for Momento data plane operations - /// - /// # Examples - /// ``` - /// # tokio_test::block_on(async { - /// use momento::CredentialProviderBuilder; - /// let credential_provider = CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string()) - /// .with_cache_endpoint("my_cache_endpoint.com".to_string()) - /// .build() - /// .expect("MOMENTO_API_KEY must be set"); - /// assert_eq!("https://my_cache_endpoint.com", credential_provider.cache_endpoint); - /// # }) - /// ``` - /// - pub fn with_cache_endpoint(mut self, cache_endpoint_override: String) -> Self { - self.cache_endpoint_override = Some(cache_endpoint_override); - self - } - - /// Override the control plane endpoint - /// # Arguments - /// - /// * `control_endpoint_override` - The host which the Momento client will connect to for Momento control plane operations - /// - /// # Examples - /// ``` - /// # tokio_test::block_on(async { - /// use momento::CredentialProviderBuilder; - /// let credential_provider = CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string()) - /// .with_control_endpoint("my_control_endpoint.com".to_string()) - /// .build() - /// .expect("MOMENTO_API_KEY must be set"); - /// assert_eq!("https://my_control_endpoint.com", credential_provider.control_endpoint); - /// # }) - /// ``` - /// - pub fn with_control_endpoint(mut self, control_endpoint_override: String) -> Self { - self.control_endpoint_override = Some(control_endpoint_override); - self - } - - /// Override both control plane and data plane endpoints - /// # Arguments - /// - /// * `endpoint_override` - The host which will be used to build control and data plane endpoints by prepending `control` and `cache` subdomains. - /// - /// # Examples - /// ``` - /// # tokio_test::block_on(async { - /// use momento::CredentialProviderBuilder; - /// let credential_provider = CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string()) - /// .with_momento_endpoint("my_endpoint.com".to_string()) - /// .build() - /// .expect("MOMENTO_API_KEY must be set"); - /// assert_eq!("https://cache.my_endpoint.com", credential_provider.cache_endpoint); - /// assert_eq!("https://control.my_endpoint.com", credential_provider.control_endpoint); - /// # }) - /// ``` - /// - pub fn with_momento_endpoint(mut self, endpoint_override: String) -> Self { - self.cache_endpoint_override = Some(CredentialProviderBuilder::get_cache_endpoint( - &endpoint_override, - )); - self.control_endpoint_override = Some(CredentialProviderBuilder::get_control_endpoint( - &endpoint_override, - )); - self - } - - pub fn build(self) -> MomentoResult { + // /// Override the data plane endpoint + // /// # Arguments + // /// + // /// * `cache_endpoint_override` - The host which the Momento client will connect to for Momento data plane operations + // /// + // /// # Examples + // /// ``` + // /// # tokio_test::block_on(async { + // /// use momento::CredentialProviderBuilder; + // /// let credential_provider = CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string()) + // /// .with_cache_endpoint("my_cache_endpoint.com".to_string()) + // /// .build() + // /// .expect("MOMENTO_API_KEY must be set"); + // /// assert_eq!("https://my_cache_endpoint.com", credential_provider.cache_endpoint); + // /// # }) + // /// ``` + // /// + // fn with_cache_endpoint(mut self, cache_endpoint_override: String) -> Self { + // self.cache_endpoint_override = Some(cache_endpoint_override); + // self + // } + + // /// Override the control plane endpoint + // /// # Arguments + // /// + // /// * `control_endpoint_override` - The host which the Momento client will connect to for Momento control plane operations + // /// + // /// # Examples + // /// ``` + // /// # tokio_test::block_on(async { + // /// use momento::CredentialProviderBuilder; + // /// let credential_provider = CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string()) + // /// .with_control_endpoint("my_control_endpoint.com".to_string()) + // /// .build() + // /// .expect("MOMENTO_API_KEY must be set"); + // /// assert_eq!("https://my_control_endpoint.com", credential_provider.control_endpoint); + // /// # }) + // /// ``` + // /// + // fn with_control_endpoint(mut self, control_endpoint_override: String) -> Self { + // self.control_endpoint_override = Some(control_endpoint_override); + // self + // } + + // /// Override both control plane and data plane endpoints + // /// # Arguments + // /// + // /// * `endpoint_override` - The host which will be used to build control and data plane endpoints by prepending `control` and `cache` subdomains. + // /// + // /// # Examples + // /// ``` + // /// # tokio_test::block_on(async { + // /// use momento::CredentialProviderBuilder; + // /// let credential_provider = CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string()) + // /// .with_momento_endpoint("my_endpoint.com".to_string()) + // /// .build() + // /// .expect("MOMENTO_API_KEY must be set"); + // /// assert_eq!("https://cache.my_endpoint.com", credential_provider.cache_endpoint); + // /// assert_eq!("https://control.my_endpoint.com", credential_provider.control_endpoint); + // /// # }) + // /// ``` + // /// + // fn with_momento_endpoint(mut self, endpoint_override: String) -> Self { + // self.cache_endpoint_override = Some(CredentialProviderBuilder::get_cache_endpoint( + // &endpoint_override, + // )); + // self.control_endpoint_override = Some(CredentialProviderBuilder::get_control_endpoint( + // &endpoint_override, + // )); + // self + // } + + fn build(self) -> MomentoResult { let token_to_process = match self.auth_token_source { EnvironmentVariable(env_var_name) => match env::var(&env_var_name) { Ok(auth_token) => auth_token, @@ -320,6 +341,7 @@ fn token_parsing_error(e: Box) -> MomentoEr #[cfg(test)] mod tests { use crate::credential_provider::CredentialProviderBuilder; + use crate::CredentialProvider; use std::env; #[test] @@ -416,66 +438,66 @@ mod tests { assert_eq!(e.to_string(), _err_msg); } - #[test] - fn valid_no_c_cp_claims_jwt_with_endpoint_overrides() { - // Token header - // ------------ - // { - // "typ": "JWT", - // "alg": "HS256" - // } - // - // Token claims - // ------------ - // { - // "iat": 1516239022, - // "name": "John Doe", - // "sub": "abcd" - // } - let auth_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhYmNkIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.PTgxba"; - let credential_provider = CredentialProviderBuilder::from_string(auth_token.to_string()) - .with_cache_endpoint("cache.help.com".to_string()) - .with_control_endpoint("control.help.com".to_string()) - .build() - .expect("should be able to get credentials"); - - assert_eq!(credential_provider.auth_token, auth_token.to_string()); - assert_eq!( - credential_provider.control_endpoint, - "https://control.help.com" - ); - assert_eq!(credential_provider.cache_endpoint, "https://cache.help.com"); - } - - #[test] - fn valid_no_c_cp_claims_jwt_with_momento_endpoint_override() { - // Token header - // ------------ - // { - // "typ": "JWT", - // "alg": "HS256" - // } - // - // Token claims - // ------------ - // { - // "iat": 1516239022, - // "name": "John Doe", - // "sub": "abcd" - // } - let auth_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhYmNkIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.PTgxba"; - let credential_provider = CredentialProviderBuilder::from_string(auth_token.to_string()) - .with_momento_endpoint("help.com".to_string()) - .build() - .expect("should be able to get credentials"); - - assert_eq!(credential_provider.auth_token, auth_token.to_string()); - assert_eq!( - credential_provider.control_endpoint, - "https://control.help.com" - ); - assert_eq!(credential_provider.cache_endpoint, "https://cache.help.com"); - } + // #[test] + // fn valid_no_c_cp_claims_jwt_with_endpoint_overrides() { + // // Token header + // // ------------ + // // { + // // "typ": "JWT", + // // "alg": "HS256" + // // } + // // + // // Token claims + // // ------------ + // // { + // // "iat": 1516239022, + // // "name": "John Doe", + // // "sub": "abcd" + // // } + // let auth_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhYmNkIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.PTgxba"; + // let credential_provider = CredentialProviderBuilder::from_string(auth_token.to_string()) + // .with_cache_endpoint("cache.help.com".to_string()) + // .with_control_endpoint("control.help.com".to_string()) + // .build() + // .expect("should be able to get credentials"); + // + // assert_eq!(credential_provider.auth_token, auth_token.to_string()); + // assert_eq!( + // credential_provider.control_endpoint, + // "https://control.help.com" + // ); + // assert_eq!(credential_provider.cache_endpoint, "https://cache.help.com"); + // } + + // #[test] + // fn valid_no_c_cp_claims_jwt_with_momento_endpoint_override() { + // // Token header + // // ------------ + // // { + // // "typ": "JWT", + // // "alg": "HS256" + // // } + // // + // // Token claims + // // ------------ + // // { + // // "iat": 1516239022, + // // "name": "John Doe", + // // "sub": "abcd" + // // } + // let auth_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhYmNkIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.PTgxba"; + // let credential_provider = CredentialProviderBuilder::from_string(auth_token.to_string()) + // .with_momento_endpoint("help.com".to_string()) + // .build() + // .expect("should be able to get credentials"); + // + // assert_eq!(credential_provider.auth_token, auth_token.to_string()); + // assert_eq!( + // credential_provider.control_endpoint, + // "https://control.help.com" + // ); + // assert_eq!(credential_provider.cache_endpoint, "https://cache.help.com"); + // } #[test] fn invalid_no_cache_claim_jwt_with_no_endpoint_override() { @@ -494,40 +516,34 @@ mod tests { // "sub": "abcd" // } let auth_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhYmNkIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.PTgxba"; - let e = CredentialProviderBuilder::from_string(auth_token.to_string()) - .with_control_endpoint("foo".to_string()) - .build() - .unwrap_err(); + let e = CredentialProvider::from_string(auth_token.to_string()).unwrap_err(); let _err_msg = "invalid argument: auth token is missing cache endpoint and endpoint override is missing. One or the other must be provided".to_string(); assert_eq!(e.to_string(), _err_msg); } - #[test] - fn invalid_no_control_claim_jwt_with_no_endpoint_override() { - // Token header - // ------------ - // { - // "typ": "JWT", - // "alg": "HS256" - // } - // - // Token claims - // ------------ - // { - // "iat": 1516239022, - // "name": "John Doe", - // "sub": "abcd" - // } - let auth_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhYmNkIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.PTgxba"; - let e = CredentialProviderBuilder::from_string(auth_token.to_string()) - .with_cache_endpoint("foo".to_string()) - .build() - .unwrap_err(); - let _err_msg = - "invalid argument: auth token is missing control endpoint and endpoint override is missing. One or the other must be provided.".to_string(); - assert_eq!(e.to_string(), _err_msg); - } + // #[test] + // fn invalid_no_control_claim_jwt_with_no_endpoint_override() { + // // Token header + // // ------------ + // // { + // // "typ": "JWT", + // // "alg": "HS256" + // // } + // // + // // Token claims + // // ------------ + // // { + // // "iat": 1516239022, + // // "name": "John Doe", + // // "sub": "abcd" + // // } + // let auth_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhYmNkIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.PTgxba"; + // let e = CredentialProvider::from_string(auth_token.to_string()).unwrap_err(); + // let _err_msg = + // "invalid argument: auth token is missing control endpoint and endpoint override is missing. One or the other must be provided.".to_string(); + // assert_eq!(e.to_string(), _err_msg); + // } #[test] fn valid_v1_token() { @@ -547,22 +563,22 @@ mod tests { assert_eq!("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0IHN1YmplY3QiLCJ2ZXIiOjEsInAiOiIifQ.hg2wMbWe-wesQVtA7wuJcRULjRphXLQwQTVYfQL3L7c", credential_provider.auth_token); } - #[test] - fn v1_token_with_overrides() { - let v1_token = "eyJlbmRwb2ludCI6Im1vbWVudG9fZW5kcG9pbnQiLCJhcGlfa2V5IjoiZXlKaGJHY2lPaUpJVXpJMU5pSjkuZXlKemRXSWlPaUowWlhOMElITjFZbXBsWTNRaUxDSjJaWElpT2pFc0luQWlPaUlpZlEuaGcyd01iV2Utd2VzUVZ0QTd3dUpjUlVMalJwaFhMUXdRVFZZZlFMM0w3YyJ9Cg==".to_string(); - - let credential_provider = CredentialProviderBuilder::from_string(v1_token) - .with_cache_endpoint("cache.foo.com".to_string()) - .with_control_endpoint("control.foo.com".to_string()) - .build() - .expect("failed to parse token"); - assert_eq!("https://cache.foo.com", credential_provider.cache_endpoint); - assert_eq!( - "https://control.foo.com", - credential_provider.control_endpoint - ); - assert_eq!("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0IHN1YmplY3QiLCJ2ZXIiOjEsInAiOiIifQ.hg2wMbWe-wesQVtA7wuJcRULjRphXLQwQTVYfQL3L7c", credential_provider.auth_token); - } + // #[test] + // fn v1_token_with_overrides() { + // let v1_token = "eyJlbmRwb2ludCI6Im1vbWVudG9fZW5kcG9pbnQiLCJhcGlfa2V5IjoiZXlKaGJHY2lPaUpJVXpJMU5pSjkuZXlKemRXSWlPaUowWlhOMElITjFZbXBsWTNRaUxDSjJaWElpT2pFc0luQWlPaUlpZlEuaGcyd01iV2Utd2VzUVZ0QTd3dUpjUlVMalJwaFhMUXdRVFZZZlFMM0w3YyJ9Cg==".to_string(); + // + // let credential_provider = CredentialProviderBuilder::from_string(v1_token) + // .with_cache_endpoint("cache.foo.com".to_string()) + // .with_control_endpoint("control.foo.com".to_string()) + // .build() + // .expect("failed to parse token"); + // assert_eq!("https://cache.foo.com", credential_provider.cache_endpoint); + // assert_eq!( + // "https://control.foo.com", + // credential_provider.control_endpoint + // ); + // assert_eq!("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0IHN1YmplY3QiLCJ2ZXIiOjEsInAiOiIifQ.hg2wMbWe-wesQVtA7wuJcRULjRphXLQwQTVYfQL3L7c", credential_provider.auth_token); + // } #[test] fn invalid_v1_token_json() { diff --git a/src/lib.rs b/src/lib.rs index 16ab110a..eb794304 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ mod grpc; mod simple_cache_client; mod utils; -pub use crate::credential_provider::{CredentialProvider, CredentialProviderBuilder}; +pub use crate::credential_provider::CredentialProvider; pub use crate::response::ErrorSource; pub use crate::response::MomentoError; pub use crate::simple_cache_client::{ diff --git a/src/preview/topics.rs b/src/preview/topics.rs index c3e1d76c..50d903b7 100644 --- a/src/preview/topics.rs +++ b/src/preview/topics.rs @@ -21,12 +21,11 @@ pub struct TopicClient { /// Work with topics, publishing and subscribing. /// ```rust /// use momento::preview::topics::TopicClient; -/// use momento::{CredentialProvider, CredentialProviderBuilder}; +/// use momento::{CredentialProvider}; /// use futures::StreamExt; /// /// async { -/// let credential_provider = CredentialProviderBuilder::from_string("token".to_string()) -/// .build() +/// let credential_provider = CredentialProvider::from_string("token".to_string()) /// .expect("could not get credentials"); /// // Get a topic client /// let client = TopicClient::connect( diff --git a/src/requests/cache/basic/get.rs b/src/requests/cache/basic/get.rs new file mode 100644 index 00000000..13d9f411 --- /dev/null +++ b/src/requests/cache/basic/get.rs @@ -0,0 +1,169 @@ +use crate::cache_client::CacheClient; +use crate::requests::cache::MomentoRequest; +use crate::simple_cache_client::prep_request; +use crate::utils::parse_string; +use crate::{IntoBytes, MomentoError, MomentoResult}; +use momento_protos::cache_client::ECacheResult; +use std::convert::{TryFrom, TryInto}; + +/// ``` +/// # fn main() -> anyhow::Result<()> { +/// # use momento_test_util::create_doctest_client; +/// # tokio_test::block_on(async { +/// # let (cache_client, cache_name) = create_doctest_client(); +/// use std::convert::TryInto; +/// use momento::requests::cache::basic::get::Get; +/// +/// let get_response = cache_client.get(&cache_name, "key").await?; +/// +/// let item: String = match get_response { +/// Get::Hit { value } => value.try_into().expect("I stored a string!"), +/// Get::Miss => return Err(anyhow::Error::msg("cache miss")) // probably you'll do something else here +/// }; +/// +/// # Ok(()) +/// # }) +/// # +/// } +/// ``` +pub struct GetRequest { + cache_name: String, + key: K, +} + +impl GetRequest { + pub fn new(cache_name: String, key: K) -> Self { + Self { cache_name, key } + } +} + +impl MomentoRequest for GetRequest { + type Response = Get; + + async fn send(self, cache_client: &CacheClient) -> MomentoResult { + let request = prep_request( + &self.cache_name, + momento_protos::cache_client::GetRequest { + cache_key: self.key.into_bytes(), + }, + )?; + + let response = cache_client + .data_client + .clone() + .get(request) + .await? + .into_inner(); + match response.result() { + ECacheResult::Hit => Ok(Get::Hit { + value: GetValue { + raw_item: response.cache_body, + }, + }), + ECacheResult::Miss => Ok(Get::Miss), + _ => unreachable!(), + } + } +} + +/// Response for a cache get operation. +/// +/// If you'd like to handle misses you can simply match and handle your response: +/// ``` +/// # use momento::response::Get; +/// # use momento::MomentoResult; +/// # let get_response = Get::Hit { value: momento::response::GetValue::new(vec![]) }; +/// use std::convert::TryInto; +/// let item: String = match get_response { +/// Get::Hit { value } => value.try_into().expect("I stored a string!"), +/// Get::Miss => return // probably you'll do something else here +/// }; +/// ``` +/// +/// Or, if you're storing raw bytes you can get at them simply: +/// ``` +/// # use momento::response::Get; +/// # use momento::MomentoResult; +/// # let get_response = Get::Hit { value: momento::response::GetValue::new(vec![]) }; +/// let item: Vec = match get_response { +/// Get::Hit { value } => value.into(), +/// Get::Miss => return // probably you'll do something else here +/// }; +/// ``` +/// +/// You can cast your result directly into a Result suitable for +/// ?-propagation if you know you are expecting a String item. +/// +/// Of course, a Miss in this case will be turned into an Error. If that's what you want, then +/// this is what you're after: +/// ``` +/// # use momento::response::Get; +/// # use momento::MomentoResult; +/// # let get_response = Get::Hit { value: momento::response::GetValue::new(vec![]) }; +/// use std::convert::TryInto; +/// let item: MomentoResult = get_response.try_into(); +/// ``` +/// +/// You can also go straight into a Vec if you prefer: +/// ``` +/// # use momento::response::Get; +/// # use momento::MomentoResult; +/// # let get_response = Get::Hit { value: momento::response::GetValue::new(vec![]) }; +/// use std::convert::TryInto; +/// let item: MomentoResult> = get_response.try_into(); +/// ``` +#[derive(Debug, PartialEq, Eq)] +pub enum Get { + Hit { value: GetValue }, + Miss, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct GetValue { + pub(crate) raw_item: Vec, +} +impl GetValue { + pub fn new(raw_item: Vec) -> Self { + Self { raw_item } + } +} + +impl TryFrom for String { + type Error = MomentoError; + + fn try_from(value: GetValue) -> Result { + parse_string(value.raw_item) + } +} + +impl From for Vec { + fn from(value: GetValue) -> Self { + value.raw_item + } +} + +impl TryFrom for String { + type Error = MomentoError; + + fn try_from(value: Get) -> Result { + match value { + Get::Hit { value } => value.try_into(), + Get::Miss => Err(MomentoError::Miss { + description: std::borrow::Cow::Borrowed("get response was a miss"), + }), + } + } +} + +impl TryFrom for Vec { + type Error = MomentoError; + + fn try_from(value: Get) -> Result { + match value { + Get::Hit { value } => Ok(value.into()), + Get::Miss => Err(MomentoError::Miss { + description: std::borrow::Cow::Borrowed("get response was a miss"), + }), + } + } +} diff --git a/src/requests/cache/basic/mod.rs b/src/requests/cache/basic/mod.rs new file mode 100644 index 00000000..e7537b1c --- /dev/null +++ b/src/requests/cache/basic/mod.rs @@ -0,0 +1,2 @@ +pub mod get; +pub mod set; diff --git a/src/requests/cache/basic/set.rs b/src/requests/cache/basic/set.rs new file mode 100644 index 00000000..be408a6d --- /dev/null +++ b/src/requests/cache/basic/set.rs @@ -0,0 +1,68 @@ +use crate::cache_client::CacheClient; +use crate::requests::cache::MomentoRequest; +use crate::simple_cache_client::prep_request_with_timeout; +use crate::{IntoBytes, MomentoResult}; +use std::time::Duration; + +/// ``` +/// # fn main() -> anyhow::Result<()> { +/// # use momento_test_util::create_doctest_client; +/// # tokio_test::block_on(async { +/// # let (cache_client, cache_name) = create_doctest_client(); +/// use momento::requests::cache::basic::set::Set; +/// +/// let set_response = cache_client.set(&cache_name, "key", "value").await?; +/// assert_eq!(set_response, Set {}); +/// # Ok(()) +/// # }) +/// # +/// } +/// ``` +pub struct SetRequest { + cache_name: String, + key: K, + value: V, + ttl: Option, +} + +impl SetRequest { + pub fn new(cache_name: String, key: K, value: V) -> Self { + let ttl = None; + Self { + cache_name, + key, + value, + ttl, + } + } + + pub fn with_ttl(mut self, ttl: Duration) -> Self { + self.ttl = Some(ttl); + self + } +} + +impl MomentoRequest for SetRequest { + type Response = Set; + + async fn send(self, cache_client: &CacheClient) -> MomentoResult { + // let ttl = self.ttl.unwrap_or_default(); + // let elements = self.elements.into_iter().map(|e| e.into_bytes()).collect(); + // let set_name = self.set_name.into_bytes(); + let request = prep_request_with_timeout( + &self.cache_name, + cache_client.configuration.deadline_millis(), + momento_protos::cache_client::SetRequest { + cache_key: self.key.into_bytes(), + cache_body: self.value.into_bytes(), + ttl_milliseconds: cache_client.expand_ttl_ms(self.ttl)?, + }, + )?; + + let _ = cache_client.data_client.clone().set(request).await?; + Ok(Set {}) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub struct Set {} diff --git a/src/requests/cache/mod.rs b/src/requests/cache/mod.rs index b01983b0..afad60ed 100644 --- a/src/requests/cache/mod.rs +++ b/src/requests/cache/mod.rs @@ -4,11 +4,9 @@ use crate::MomentoResult; pub mod create_cache; pub mod delete_cache; -pub mod set_add_elements; -pub mod sorted_set_fetch_by_rank; -pub mod sorted_set_fetch_by_score; -pub mod sorted_set_put_element; -pub mod sorted_set_put_elements; +pub mod basic; +pub mod set; +pub mod sorted_set; pub trait MomentoRequest { type Response; diff --git a/src/requests/cache/set/mod.rs b/src/requests/cache/set/mod.rs new file mode 100644 index 00000000..ad3f5cca --- /dev/null +++ b/src/requests/cache/set/mod.rs @@ -0,0 +1 @@ +pub mod set_add_elements; diff --git a/src/requests/cache/set_add_elements.rs b/src/requests/cache/set/set_add_elements.rs similarity index 90% rename from src/requests/cache/set_add_elements.rs rename to src/requests/cache/set/set_add_elements.rs index 9d2a4112..6c468761 100644 --- a/src/requests/cache/set_add_elements.rs +++ b/src/requests/cache/set/set_add_elements.rs @@ -9,12 +9,11 @@ use crate::{CollectionTtl, IntoBytes, MomentoResult}; /// # fn main() -> anyhow::Result<()> { /// # tokio_test::block_on(async { /// use std::time::Duration; -/// use momento::{CredentialProviderBuilder}; -/// use momento::requests::cache::set_add_elements::SetAddElements; +/// use momento::{CredentialProvider}; +/// use momento::requests::cache::set::set_add_elements::SetAddElements; /// use momento::config::configurations; /// -/// let credential_provider = CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string()) -/// .build()?; +/// let credential_provider = CredentialProvider::from_env_var("MOMENTO_API_KEY".to_string())?; /// let cache_name = "cache"; /// /// let cache_client = momento::CacheClient::new(credential_provider, configurations::laptop::latest(), Duration::from_secs(5))?; diff --git a/src/requests/cache/sorted_set/mod.rs b/src/requests/cache/sorted_set/mod.rs new file mode 100644 index 00000000..addb6844 --- /dev/null +++ b/src/requests/cache/sorted_set/mod.rs @@ -0,0 +1,4 @@ +pub mod sorted_set_fetch_by_rank; +pub mod sorted_set_fetch_by_score; +pub mod sorted_set_put_element; +pub mod sorted_set_put_elements; diff --git a/src/requests/cache/sorted_set_fetch_by_rank.rs b/src/requests/cache/sorted_set/sorted_set_fetch_by_rank.rs similarity index 94% rename from src/requests/cache/sorted_set_fetch_by_rank.rs rename to src/requests/cache/sorted_set/sorted_set_fetch_by_rank.rs index cd9dd202..900c441b 100644 --- a/src/requests/cache/sorted_set_fetch_by_rank.rs +++ b/src/requests/cache/sorted_set/sorted_set_fetch_by_rank.rs @@ -1,7 +1,6 @@ use momento_protos::cache_client::sorted_set_fetch_request::{by_index, ByIndex, Range}; use momento_protos::cache_client::{SortedSetFetchRequest, Unbounded}; -use crate::requests::cache::sorted_set_fetch_by_rank::SortOrder::Ascending; use crate::requests::cache::MomentoRequest; use crate::response::cache::sorted_set_fetch::SortedSetFetch; use crate::simple_cache_client::prep_request_with_timeout; @@ -37,8 +36,8 @@ pub enum SortOrder { /// # use std::convert::TryInto; /// # use momento_test_util::create_doctest_client; /// # tokio_test::block_on(async { -/// use momento::requests::cache::sorted_set_fetch_by_rank::SortOrder; -/// use momento::requests::cache::sorted_set_fetch_by_rank::SortedSetFetchByRankRequest; +/// use momento::requests::cache::sorted_set::sorted_set_fetch_by_rank::SortOrder; +/// use momento::requests::cache::sorted_set::sorted_set_fetch_by_rank::SortedSetFetchByRankRequest; /// use momento::response::cache::sorted_set_fetch::SortedSetFetch; /// # let (cache_client, cache_name) = create_doctest_client(); /// let sorted_set_name = "sorted_set"; @@ -77,7 +76,7 @@ impl SortedSetFetchByRankRequest { sorted_set_name, start_rank: None, end_rank: None, - order: Ascending, + order: SortOrder::Ascending, } } diff --git a/src/requests/cache/sorted_set_fetch_by_score.rs b/src/requests/cache/sorted_set/sorted_set_fetch_by_score.rs similarity index 94% rename from src/requests/cache/sorted_set_fetch_by_score.rs rename to src/requests/cache/sorted_set/sorted_set_fetch_by_score.rs index d75d9e1a..492b5a41 100644 --- a/src/requests/cache/sorted_set_fetch_by_score.rs +++ b/src/requests/cache/sorted_set/sorted_set_fetch_by_score.rs @@ -2,8 +2,8 @@ use momento_protos::cache_client::sorted_set_fetch_request::by_score::Score; use momento_protos::cache_client::sorted_set_fetch_request::{by_score, ByScore, Range}; use momento_protos::cache_client::{SortedSetFetchRequest, Unbounded}; -use crate::requests::cache::sorted_set_fetch_by_rank::SortOrder; -use crate::requests::cache::sorted_set_fetch_by_rank::SortOrder::Ascending; +use crate::requests::cache::sorted_set::sorted_set_fetch_by_rank::SortOrder; +use crate::requests::cache::sorted_set::sorted_set_fetch_by_rank::SortOrder::Ascending; use crate::requests::cache::MomentoRequest; use crate::response::cache::sorted_set_fetch::SortedSetFetch; use crate::simple_cache_client::prep_request_with_timeout; @@ -36,8 +36,8 @@ use crate::{CacheClient, IntoBytes, MomentoResult}; /// # use std::convert::TryInto; /// # use momento_test_util::create_doctest_client; /// # tokio_test::block_on(async { -/// use momento::requests::cache::sorted_set_fetch_by_rank::SortOrder; -/// use momento::requests::cache::sorted_set_fetch_by_score::SortedSetFetchByScoreRequest; +/// use momento::requests::cache::sorted_set::sorted_set_fetch_by_rank::SortOrder; +/// use momento::requests::cache::sorted_set::sorted_set_fetch_by_score::SortedSetFetchByScoreRequest; /// use momento::response::cache::sorted_set_fetch::SortedSetFetch; /// # let (cache_client, cache_name) = create_doctest_client(); /// let sorted_set_name = "sorted_set"; diff --git a/src/requests/cache/sorted_set_put_element.rs b/src/requests/cache/sorted_set/sorted_set_put_element.rs similarity index 94% rename from src/requests/cache/sorted_set_put_element.rs rename to src/requests/cache/sorted_set/sorted_set_put_element.rs index 0d57c053..e0819dec 100644 --- a/src/requests/cache/sorted_set_put_element.rs +++ b/src/requests/cache/sorted_set/sorted_set_put_element.rs @@ -25,8 +25,8 @@ use crate::{CacheClient, CollectionTtl, IntoBytes, MomentoResult}; /// # use momento_test_util::create_doctest_client; /// # tokio_test::block_on(async { /// use momento::CollectionTtl; -/// use momento::requests::cache::sorted_set_put_element::SortedSetPutElement; -/// use momento::requests::cache::sorted_set_put_element::SortedSetPutElementRequest; +/// use momento::requests::cache::sorted_set::sorted_set_put_element::SortedSetPutElement; +/// use momento::requests::cache::sorted_set::sorted_set_put_element::SortedSetPutElementRequest; /// # let (cache_client, cache_name) = create_doctest_client(); /// let sorted_set_name = "sorted_set"; /// diff --git a/src/requests/cache/sorted_set_put_elements.rs b/src/requests/cache/sorted_set/sorted_set_put_elements.rs similarity index 94% rename from src/requests/cache/sorted_set_put_elements.rs rename to src/requests/cache/sorted_set/sorted_set_put_elements.rs index cff90ee7..da26588c 100644 --- a/src/requests/cache/sorted_set_put_elements.rs +++ b/src/requests/cache/sorted_set/sorted_set_put_elements.rs @@ -24,8 +24,8 @@ use crate::{CacheClient, CollectionTtl, IntoBytes, MomentoResult}; /// # use momento_test_util::create_doctest_client; /// # tokio_test::block_on(async { /// use momento::CollectionTtl; -/// use momento::requests::cache::sorted_set_put_elements::SortedSetPutElements; -/// use momento::requests::cache::sorted_set_put_elements::SortedSetPutElementsRequest; +/// use momento::requests::cache::sorted_set::sorted_set_put_elements::SortedSetPutElements; +/// use momento::requests::cache::sorted_set::sorted_set_put_elements::SortedSetPutElementsRequest; /// # let (cache_client, cache_name) = create_doctest_client(); /// let sorted_set_name = "sorted_set"; /// diff --git a/src/response/cache/sorted_set_fetch.rs b/src/response/cache/sorted_set_fetch.rs index 7feec9f5..feea2901 100644 --- a/src/response/cache/sorted_set_fetch.rs +++ b/src/response/cache/sorted_set_fetch.rs @@ -6,6 +6,8 @@ use momento_protos::cache_client::SortedSetFetchResponse; use crate::{MomentoError, MomentoResult}; +// TODO this needs to be moved to the requests directory + #[derive(Debug, PartialEq)] pub enum SortedSetFetch { Hit { elements: SortedSetElements }, diff --git a/src/simple_cache_client.rs b/src/simple_cache_client.rs index 89ff96dd..12dcd126 100644 --- a/src/simple_cache_client.rs +++ b/src/simple_cache_client.rs @@ -167,10 +167,9 @@ impl SimpleCacheClientBuilder { /// /// ``` /// # tokio_test::block_on(async { - /// use momento::{CredentialProviderBuilder, SimpleCacheClientBuilder}; + /// use momento::{CredentialProvider, SimpleCacheClientBuilder}; /// use std::time::Duration; - /// let credential_provider = CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string()) - /// .build() + /// let credential_provider = CredentialProvider::from_env_var("MOMENTO_API_KEY".to_string()) /// .expect("MOMENTO_API_KEY must be set"); /// let momento = SimpleCacheClientBuilder::new(credential_provider, Duration::from_secs(30)) /// .expect("could not create a client") @@ -282,10 +281,9 @@ impl SimpleCacheClient { /// # tokio_test::block_on(async { /// use uuid::Uuid; /// use std::time::Duration; - /// use momento::{CredentialProviderBuilder, SimpleCacheClientBuilder}; + /// use momento::{CredentialProvider, SimpleCacheClientBuilder}; /// - /// let credential_provider = CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string()) - /// .build() + /// let credential_provider = CredentialProvider::from_env_var("MOMENTO_API_KEY".to_string()) /// .expect("MOMENTO_API_KEY must be set"); /// let cache_name = "rust-sdk-".to_string() + &Uuid::new_v4().to_string(); /// let mut momento = SimpleCacheClientBuilder::new(credential_provider, Duration::from_secs(5))? @@ -316,10 +314,9 @@ impl SimpleCacheClient { /// # use futures::FutureExt; /// use uuid::Uuid; /// use std::time::Duration; - /// use momento::{CredentialProviderBuilder, SimpleCacheClientBuilder}; + /// use momento::{CredentialProvider, SimpleCacheClientBuilder}; /// - /// let credential_provider = CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string()) - /// .build() + /// let credential_provider = CredentialProvider::from_env_var("MOMENTO_API_KEY".to_string()) /// .expect("MOMENTO_API_KEY must be set"); /// let cache_name = "rust-sdk-".to_string() + &Uuid::new_v4().to_string(); /// let mut momento = SimpleCacheClientBuilder::new(credential_provider, Duration::from_secs(5))? @@ -421,11 +418,10 @@ impl SimpleCacheClient { /// # use futures::FutureExt; /// use uuid::Uuid; /// use std::time::Duration; - /// use momento::{CredentialProviderBuilder, SimpleCacheClientBuilder}; + /// use momento::{CredentialProvider, SimpleCacheClientBuilder}; /// /// let ttl_minutes = 10; - /// let credential_provider = CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string()) - /// .build() + /// let credential_provider = CredentialProvider::from_env_var("MOMENTO_API_KEY".to_string()) /// .expect("MOMENTO_API_KEY must be set"); /// let mut momento = SimpleCacheClientBuilder::new(credential_provider, Duration::from_secs(5))? /// .build(); diff --git a/src/utils.rs b/src/utils.rs index 768eec40..9ba0c2a7 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -12,7 +12,7 @@ use std::time::{self, Duration}; const VERSION: &str = env!("CARGO_PKG_VERSION"); -pub fn is_ttl_valid(ttl: Duration) -> MomentoResult<()> { +pub(crate) fn is_ttl_valid(ttl: Duration) -> MomentoResult<()> { let max_ttl = Duration::from_millis(u64::MAX); if ttl > max_ttl { return Err(MomentoError::InvalidArgument { @@ -28,7 +28,7 @@ pub fn is_ttl_valid(ttl: Duration) -> MomentoResult<()> { Ok(()) } -pub fn is_cache_name_valid(cache_name: &str) -> Result<(), MomentoError> { +pub(crate) fn is_cache_name_valid(cache_name: &str) -> Result<(), MomentoError> { if cache_name.trim().is_empty() { return Err(MomentoError::InvalidArgument { description: "Cache name cannot be empty".into(), @@ -38,7 +38,7 @@ pub fn is_cache_name_valid(cache_name: &str) -> Result<(), MomentoError> { Ok(()) } -pub fn is_key_id_valid(key_id: &str) -> Result<(), MomentoError> { +pub(crate) fn is_key_id_valid(key_id: &str) -> Result<(), MomentoError> { if key_id.trim().is_empty() { return Err(MomentoError::InvalidArgument { description: "Key ID cannot be empty".into(), @@ -100,6 +100,13 @@ pub(crate) fn connect_channel_lazily_configurable( Ok(endpoint.connect_lazy()) } -pub fn user_agent(user_agent_name: &str) -> String { +pub(crate) fn user_agent(user_agent_name: &str) -> String { format!("rust-{user_agent_name}:{VERSION}") } + +pub(crate) fn parse_string(raw: Vec) -> MomentoResult { + String::from_utf8(raw).map_err(|e| MomentoError::TypeError { + description: std::borrow::Cow::Borrowed("item is not a utf-8 string"), + source: Box::new(e), + }) +} diff --git a/test-util/src/lib.rs b/test-util/src/lib.rs index d700e674..fff25537 100644 --- a/test-util/src/lib.rs +++ b/test-util/src/lib.rs @@ -5,8 +5,8 @@ use std::time::Duration; use uuid::Uuid; use momento::config::configurations; +use momento::CredentialProvider; use momento::{CacheClient, SimpleCacheClientBuilder}; -use momento::{CredentialProvider, CredentialProviderBuilder}; pub type DoctestResult = anyhow::Result<()>; @@ -29,10 +29,8 @@ where let _guard = runtime.enter(); let cache_name = "rust-sdk-".to_string() + &Uuid::new_v4().to_string(); - let credential_provider = - CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string()) - .build() - .expect("MOMENTO_API_KEY must be set"); + let credential_provider = CredentialProvider::from_env_var("MOMENTO_API_KEY".to_string()) + .expect("MOMENTO_API_KEY must be set"); let mut client = SimpleCacheClientBuilder::new(credential_provider.clone(), Duration::from_secs(5))?.build(); @@ -70,7 +68,6 @@ pub fn get_test_cache_name() -> String { } pub fn get_test_credential_provider() -> CredentialProvider { - CredentialProviderBuilder::from_environment_variable("MOMENTO_API_KEY".to_string()) - .build() + CredentialProvider::from_env_var("MOMENTO_API_KEY".to_string()) .expect("auth token should be valid") } diff --git a/tests/cache_sorted_set_tests.rs b/tests/cache_sorted_set_tests.rs index 3d17a263..6aa8bc3b 100644 --- a/tests/cache_sorted_set_tests.rs +++ b/tests/cache_sorted_set_tests.rs @@ -1,8 +1,10 @@ use uuid::Uuid; -use momento::requests::cache::sorted_set_fetch_by_rank::SortOrder::{Ascending, Descending}; -use momento::requests::cache::sorted_set_fetch_by_rank::SortedSetFetchByRankRequest; -use momento::requests::cache::sorted_set_fetch_by_score::SortedSetFetchByScoreRequest; +use momento::requests::cache::sorted_set::sorted_set_fetch_by_rank::SortOrder::{ + Ascending, Descending, +}; +use momento::requests::cache::sorted_set::sorted_set_fetch_by_rank::SortedSetFetchByRankRequest; +use momento::requests::cache::sorted_set::sorted_set_fetch_by_score::SortedSetFetchByScoreRequest; use momento::response::cache::sorted_set_fetch::SortedSetFetch; use crate::cache_test_state::TEST_STATE; diff --git a/tests/integration_test.rs b/tests/integration_test.rs index c893fee6..03f5968f 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -3,7 +3,7 @@ mod tests { use std::{env, time::Duration}; use momento::response::{Get, GetValue}; - use momento::{CredentialProviderBuilder, SimpleCacheClient}; + use momento::{CredentialProvider, SimpleCacheClient}; use momento::{MomentoError, SimpleCacheClientBuilder}; use serde_json::Value; use tokio::time::sleep; @@ -19,9 +19,7 @@ mod tests { auth_token: String, ) -> Result { SimpleCacheClientBuilder::new_with_explicit_agent_name( - CredentialProviderBuilder::from_string(auth_token) - .build() - .expect("auth token should be valid"), + CredentialProvider::from_string(auth_token)?, Duration::from_secs(5), "integration_test", ) From 7e2441b8ce2eb738089c797f1b27db43c0647923 Mon Sep 17 00:00:00 2001 From: Chris Price Date: Mon, 18 Mar 2024 16:42:08 -0700 Subject: [PATCH 2/4] Update src/credential_provider.rs Co-authored-by: Nate Anderson --- src/credential_provider.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/credential_provider.rs b/src/credential_provider.rs index 4d177330..32405b34 100644 --- a/src/credential_provider.rs +++ b/src/credential_provider.rs @@ -32,7 +32,7 @@ pub struct CredentialProvider { } impl CredentialProvider { - /// Returns a builder to produce a Credential Provider using an API key stored in the specified + /// Returns a Credential Provider using an API key stored in the specified /// environment variable /// /// # Arguments From 1cac77c6d7d144c06685f85cf23715a1bd3ed2f1 Mon Sep 17 00:00:00 2001 From: Chris Price Date: Mon, 18 Mar 2024 16:42:45 -0700 Subject: [PATCH 3/4] Update src/credential_provider.rs Co-authored-by: Nate Anderson --- src/credential_provider.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/credential_provider.rs b/src/credential_provider.rs index 32405b34..c72b9567 100644 --- a/src/credential_provider.rs +++ b/src/credential_provider.rs @@ -52,7 +52,7 @@ impl CredentialProvider { CredentialProviderBuilder::from_environment_variable(env_var_name).build() } - /// Returns a builder to produce a Credential Provider from the provided API key + /// Returns a Credential Provider from the provided API key /// /// # Arguments /// From a82609ceedbd3336bc109c3f2b3454a1e1d9dcad Mon Sep 17 00:00:00 2001 From: Chris Price Date: Mon, 18 Mar 2024 17:34:29 -0700 Subject: [PATCH 4/4] chore: minor cleanups from PR feedback --- example/Cargo.toml | 5 +++-- example/src/main.rs | 3 +++ example/src/readme.rs | 27 +++++++++++++++++++++++++-- src/cache_client.rs | 1 - src/credential_provider.rs | 24 ++++++++++++------------ src/requests/cache/basic/set.rs | 3 --- 6 files changed, 43 insertions(+), 20 deletions(-) diff --git a/example/Cargo.toml b/example/Cargo.toml index 329dd702..a4f8a335 100644 --- a/example/Cargo.toml +++ b/example/Cargo.toml @@ -8,5 +8,6 @@ edition = "2021" [workspace] [dependencies] -momento = "0.15.0" -tokio = { version = "1.18.2", features = ["full"] } \ No newline at end of file +#momento = "0.15.0" +momento = { path = "../" } +tokio = { version = "1.18.2", features = ["full"] } diff --git a/example/src/main.rs b/example/src/main.rs index cf7731c2..d39e7273 100644 --- a/example/src/main.rs +++ b/example/src/main.rs @@ -5,6 +5,9 @@ use momento::simple_cache_client::SimpleCacheClientBuilder; use std::env; use std::num::NonZeroU64; use std::process; +use std::time::Duration; +use momento::{CacheClient, CredentialProvider}; +use momento::config::configurations::laptop; #[tokio::main] async fn main() { diff --git a/example/src/readme.rs b/example/src/readme.rs index b9a9b278..06df900a 100644 --- a/example/src/readme.rs +++ b/example/src/readme.rs @@ -1,3 +1,26 @@ -pub fn main() { - // TODO: add example code here +use std::time::Duration; +use momento::{CacheClient, CredentialProvider}; +use momento::config::configurations::laptop; + +const CACHE_NAME: String = "cache"; + +pub async fn main() { + let cache_client = CacheClient::new( + CredentialProvider::from_env_var("MOMENTO_API_KEY")?, + laptop::latest(), + Duration::from_secs(60) + )?; + + cache_client.create_cache(CACHE_NAME).await?; + + match(cache_client.set(CACHE_NAME, "mykey", "myvalue").await) { + Ok(_) => println!("Successfully stored key 'mykey' with value 'myvalue'"), + Err(e) => println!("Error: {}", e) + } + + let value = match(cache_client.get(CACHE_NAME, "mykey").await?) { + + }; + + } \ No newline at end of file diff --git a/src/cache_client.rs b/src/cache_client.rs index af769eb1..63e8aa4f 100644 --- a/src/cache_client.rs +++ b/src/cache_client.rs @@ -172,7 +172,6 @@ impl CacheClient { /// * `cache_name` - name of cache /// * `key` - key of the item whose value we are setting /// * `value` - data to stored in the cache item - /// * `ttl` - The TTL to use for the /// /// # Examples /// diff --git a/src/credential_provider.rs b/src/credential_provider.rs index c72b9567..82789e0a 100644 --- a/src/credential_provider.rs +++ b/src/credential_provider.rs @@ -42,9 +42,9 @@ impl CredentialProvider { /// /// ``` /// # tokio_test::block_on(async { - /// use momento::CredentialProvider; - /// let credential_provider = CredentialProvider::from_env_var("MOMENTO_API_KEY".to_string()) - /// .expect("MOMENTO_API_KEY must be set"); + /// use momento::CredentialProvider; + /// let credential_provider = CredentialProvider::from_env_var("MOMENTO_API_KEY".to_string()) + /// .expect("MOMENTO_API_KEY must be set"); /// # }) /// ``` /// @@ -63,16 +63,16 @@ impl CredentialProvider { /// # use momento::MomentoResult; /// # fn main() -> () { /// # tokio_test::block_on(async { - /// use momento::CredentialProvider; + /// use momento::CredentialProvider; /// - /// let api_key = "YOUR API KEY GOES HERE"; - /// let credential_provider = match(CredentialProvider::from_string(api_key.to_string())) { - /// Ok(credential_provider) => credential_provider, - /// Err(e) => { - /// println!("Error while creating credential provider: {}", e); - /// return // probably you will do something else here - /// } - /// }; + /// let api_key = "YOUR API KEY GOES HERE"; + /// let credential_provider = match(CredentialProvider::from_string(api_key.to_string())) { + /// Ok(credential_provider) => credential_provider, + /// Err(e) => { + /// println!("Error while creating credential provider: {}", e); + /// return // probably you will do something else here + /// } + /// }; /// /// # () /// # }) diff --git a/src/requests/cache/basic/set.rs b/src/requests/cache/basic/set.rs index be408a6d..51c7df00 100644 --- a/src/requests/cache/basic/set.rs +++ b/src/requests/cache/basic/set.rs @@ -46,9 +46,6 @@ impl MomentoRequest for SetRequest { type Response = Set; async fn send(self, cache_client: &CacheClient) -> MomentoResult { - // let ttl = self.ttl.unwrap_or_default(); - // let elements = self.elements.into_iter().map(|e| e.into_bytes()).collect(); - // let set_name = self.set_name.into_bytes(); let request = prep_request_with_timeout( &self.cache_name, cache_client.configuration.deadline_millis(),