diff --git a/src/client.rs b/src/client.rs index 450bb583..bd1e2812 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,7 +1,7 @@ use crate::{ errors::*, indexes::*, - key::{Key, KeyBuilder}, + key::{Key, KeyBuilder, KeyUpdater, KeysQuery, KeysResults}, request::*, task_info::TaskInfo, tasks::{Task, TasksQuery, TasksResults}, @@ -309,6 +309,40 @@ impl Client { } } + /// Get the API [Key]s from Meilisearch with parameters. + /// See the [meilisearch documentation](https://docs.meilisearch.com/reference/api/keys.html#get-all-keys). + /// + /// See also [Client::create_key] and [Client::get_key]. + /// + /// # Example + /// + /// ``` + /// # use meilisearch_sdk::{client::*, errors::Error, key::KeysQuery}; + /// # + /// # let MEILISEARCH_HOST = option_env!("MEILISEARCH_HOST").unwrap_or("http://localhost:7700"); + /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey"); + /// # + /// # futures::executor::block_on(async move { + /// let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); + /// let mut query = KeysQuery::new(); + /// query.with_limit(1); + /// let keys = client.get_keys_with(&query).await.unwrap(); + /// + /// assert_eq!(keys.results.len(), 1); + /// # }); + /// ``` + pub async fn get_keys_with(&self, keys_query: &KeysQuery) -> Result { + let keys = request::<&KeysQuery, KeysResults>( + &format!("{}/keys", self.host), + &self.api_key, + Method::Get(keys_query), + 200, + ) + .await?; + + Ok(keys) + } + /// Get the API [Key]s from Meilisearch. /// See the [meilisearch documentation](https://docs.meilisearch.com/reference/api/keys.html#get-all-keys). /// @@ -325,18 +359,12 @@ impl Client { /// # futures::executor::block_on(async move { /// let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); /// let keys = client.get_keys().await.unwrap(); - /// assert!(keys.len() >= 2); + /// + /// assert_eq!(keys.results.len(), 2); /// # }); /// ``` - pub async fn get_keys(&self) -> Result, Error> { - #[derive(Deserialize)] - #[serde(rename_all = "camelCase")] - pub struct Keys { - #[serde(rename = "results")] - pub inner: Vec, - } - - let keys = request::<(), Keys>( + pub async fn get_keys(&self) -> Result { + let keys = request::<(), KeysResults>( &format!("{}/keys", self.host), &self.api_key, Method::Get(()), @@ -344,7 +372,7 @@ impl Client { ) .await?; - Ok(keys.inner) + Ok(keys) } /// Get one API [Key] from Meilisearch. @@ -362,11 +390,12 @@ impl Client { /// # /// # futures::executor::block_on(async move { /// let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); - /// # let key = client.get_keys().await.unwrap().into_iter().find(|k| k.description.starts_with("Default Search API Key")).unwrap(); - /// let key_id = // enter your API key here, for the example we'll say we entered our search API key. - /// # key.key; + /// # let key = client.get_keys().await.unwrap().results.into_iter() + /// .find(|k| k.name.as_ref().map_or(false, |name| name.starts_with("Default Search API Key"))); + /// let key_id = key.unwrap().key // enter your API key here, for the example we use the search API key. /// let key = client.get_key(key_id).await.unwrap(); - /// assert_eq!(key.description, "Default Search API Key (Use it to search from the frontend)"); + /// + /// assert_eq!(key.name, Some("Default Search API Key".to_string())); /// # }); /// ``` pub async fn get_key(&self, key: impl AsRef) -> Result { @@ -394,14 +423,14 @@ impl Client { /// # /// # futures::executor::block_on(async move { /// let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); - /// let key = KeyBuilder::new("delete_key"); + /// let key = KeyBuilder::new(); /// let key = client.create_key(key).await.unwrap(); /// let inner_key = key.key.clone(); /// /// client.delete_key(key).await.unwrap(); /// /// let keys = client.get_keys().await.unwrap(); - /// assert!(keys.iter().all(|key| key.key != inner_key)); + /// assert!(keys.results.iter().all(|key| key.key != inner_key)); /// # }); /// ``` pub async fn delete_key(&self, key: impl AsRef) -> Result<(), Error> { @@ -429,11 +458,12 @@ impl Client { /// # /// # futures::executor::block_on(async move { /// let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); - /// let mut key = KeyBuilder::new("create_key"); - /// key.with_index("*").with_action(Action::DocumentsAdd); - /// let key = client.create_key(key).await.unwrap(); + /// let name = "create_key".to_string(); + /// let mut key = KeyBuilder::new(); + /// key.with_name(&name); /// - /// assert_eq!(key.description, "create_key"); + /// let key = client.create_key(key).await.unwrap(); + /// assert_eq!(key.name, Some(name)); /// # client.delete_key(key).await.unwrap(); /// # }); /// ``` @@ -455,25 +485,26 @@ impl Client { /// # Example /// /// ``` - /// # use meilisearch_sdk::{client::*, errors::Error, key::KeyBuilder}; + /// # use meilisearch_sdk::{client::*, errors::Error, key::KeyBuilder, key::KeyUpdater}; /// # /// # let MEILISEARCH_HOST = option_env!("MEILISEARCH_HOST").unwrap_or("http://localhost:7700"); /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey"); /// # /// # futures::executor::block_on(async move { /// let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); - /// let key = KeyBuilder::new("update_key"); - /// let mut key = client.create_key(key).await.unwrap(); - /// assert!(key.indexes.is_empty()); + /// let new_key = KeyBuilder::new(); + /// let name = "my name".to_string(); + /// let mut new_key = client.create_key(new_key).await.unwrap(); + /// let mut key_update = KeyUpdater::new(new_key); + /// key_update.with_name(&name); /// - /// key.indexes = vec!["*".to_string()]; - /// let key = client.update_key(key).await.unwrap(); - /// assert_eq!(key.indexes, vec!["*"]); + /// let key = client.update_key(key_update).await.unwrap(); + /// assert_eq!(key.name, Some(name)); /// # client.delete_key(key).await.unwrap(); /// # }); /// ``` - pub async fn update_key(&self, key: impl AsRef) -> Result { - request::<&Key, Key>( + pub async fn update_key(&self, key: impl AsRef) -> Result { + request::<&KeyUpdater, Key>( &format!("{}/keys/{}", self.host, key.as_ref().key), &self.api_key, Method::Patch(key.as_ref()), @@ -814,33 +845,35 @@ mod tests { async fn test_get_tasks_with_params(client: Client) { let query = TasksQuery::new(&client); let tasks = client.get_tasks_with(&query).await.unwrap(); + assert!(tasks.results.len() >= 2); } #[meilisearch_test] async fn test_get_keys(client: Client) { let keys = client.get_keys().await.unwrap(); - assert!(keys.len() >= 2); - assert!(keys.iter().any( - |k| k.description != "Default Search API Key (Use it to search from the frontend)" - )); - assert!(keys.iter().any( - |k| k.description != "Default Admin API Key (Use it for all other operations. Caution! Do not use it on a public frontend)" - )); + + assert!(keys.results.len() >= 2); } #[meilisearch_test] - async fn test_delete_key(client: Client, description: String) { - let key = KeyBuilder::new(description); + async fn test_delete_key(client: Client, name: String) { + let mut key = KeyBuilder::new(); + key.with_name(&name); let key = client.create_key(key).await.unwrap(); client.delete_key(&key).await.unwrap(); - let keys = client.get_keys().await.unwrap(); - assert!(keys.iter().all(|k| k.key != key.key)); + let keys = KeysQuery::new() + .with_limit(10000) + .execute(&client) + .await + .unwrap(); + + assert!(keys.results.iter().all(|k| k.key != key.key)); } #[meilisearch_test] - async fn test_error_delete_key(mut client: Client, description: String) { + async fn test_error_delete_key(mut client: Client, name: String) { // ==> accessing a key that does not exist let error = client.delete_key("invalid_key").await.unwrap_err(); assert!(matches!( @@ -853,7 +886,8 @@ mod tests { )); // ==> executing the action without enough right - let key = KeyBuilder::new(description); + let mut key = KeyBuilder::new(); + key.with_name(&name); let key = client.create_key(key).await.unwrap(); let master_key = client.api_key.clone(); @@ -886,16 +920,18 @@ mod tests { } #[meilisearch_test] - async fn test_create_key(client: Client, description: String) { + async fn test_create_key(client: Client, name: String) { let expires_at = OffsetDateTime::now_utc() + time::Duration::HOUR; - let mut key = KeyBuilder::new(description.clone()); + let mut key = KeyBuilder::new(); key.with_action(Action::DocumentsAdd) + .with_name(&name) .with_expires_at(expires_at.clone()) + .with_description("a description") .with_index("*"); let key = client.create_key(key).await.unwrap(); assert_eq!(key.actions, vec![Action::DocumentsAdd]); - assert_eq!(key.description, description); + assert_eq!(&key.name, &Some(name)); // We can't compare the two timestamp directly because of some nanoseconds imprecision with the floats assert_eq!( key.expires_at.unwrap().unix_timestamp(), @@ -903,27 +939,14 @@ mod tests { ); assert_eq!(key.indexes, vec!["*".to_string()]); - let keys = client.get_keys().await.unwrap(); - - let remote_key = keys.iter().find(|k| k.key == key.key).unwrap(); - - assert_eq!(remote_key.actions, vec![Action::DocumentsAdd]); - assert_eq!(remote_key.description, description); - // We can't compare the two timestamp directly because of some nanoseconds imprecision with the floats - assert_eq!( - remote_key.expires_at.unwrap().unix_timestamp(), - expires_at.unix_timestamp() - ); - assert_eq!(remote_key.indexes, vec!["*".to_string()]); - client.delete_key(key).await.unwrap(); } #[meilisearch_test] - async fn test_error_create_key(mut client: Client, description: String) { + async fn test_error_create_key(mut client: Client, name: String) { // ==> Invalid index name /* TODO: uncomment once meilisearch fix this bug: https://github.com/meilisearch/meilisearch/issues/2158 - let mut key = KeyBuilder::new(&description); + let mut key = KeyBuilder::new(); key.with_index("invalid index # / \\name with spaces"); let error = client.create_key(key).await.unwrap_err(); @@ -938,14 +961,16 @@ mod tests { */ // ==> executing the action without enough right - let no_right_key = KeyBuilder::new(&description); + let mut no_right_key = KeyBuilder::new(); + no_right_key.with_name(&format!("{name}_1")); let no_right_key = client.create_key(no_right_key).await.unwrap(); // backup the master key for cleanup at the end of the test let master_client = client.clone(); client.api_key = Arc::new(no_right_key.key.clone()); - let key = KeyBuilder::new(&description); + let mut key = KeyBuilder::new(); + key.with_name(format!("{name}_2")); let error = client.create_key(key).await.unwrap_err(); assert!(matches!( @@ -963,84 +988,22 @@ mod tests { #[meilisearch_test] async fn test_update_key(client: Client, description: String) { - let expires_at = OffsetDateTime::now_utc() + time::Duration::HOUR; - let key = KeyBuilder::new(description.clone()); + let mut key = KeyBuilder::new(); + key.with_name("test_update_key"); let mut key = client.create_key(key).await.unwrap(); - key.actions = vec![Action::DocumentsAdd]; - key.expires_at = Some(expires_at); - key.indexes = vec!["*".to_string()]; - - let key = client.update_key(key).await.unwrap(); - - assert_eq!(key.actions, vec![Action::DocumentsAdd]); - assert_eq!(key.description, description); - // We can't compare the two timestamp directly because of some nanoseconds imprecision with the floats - assert_eq!( - key.expires_at.unwrap().unix_timestamp(), - expires_at.unix_timestamp() - ); - assert_eq!(key.indexes, vec!["*".to_string()]); + let name = "new name".to_string(); + key.with_description(&description); + key.with_name(&name); - let keys = client.get_keys().await.unwrap(); - - let remote_key = keys.iter().find(|k| k.key == key.key).unwrap(); + let key = key.update(&client).await.unwrap(); - assert_eq!(remote_key.actions, vec![Action::DocumentsAdd]); - assert_eq!(remote_key.description, description); - // We can't compare the two timestamp directly because of some nanoseconds imprecision with the floats - assert_eq!( - remote_key.expires_at.unwrap().unix_timestamp(), - expires_at.unix_timestamp() - ); - assert_eq!(remote_key.indexes, vec!["*".to_string()]); + assert_eq!(key.description, Some(description)); + assert_eq!(key.name, Some(name)); client.delete_key(key).await.unwrap(); } - #[meilisearch_test] - async fn test_error_update_key(mut client: Client, description: String) { - let key = KeyBuilder::new(description.clone()); - let key = client.create_key(key).await.unwrap(); - - // ==> Invalid index name - /* TODO: uncomment once meilisearch fix this bug: https://github.com/meilisearch/meilisearch/issues/2158 - key.indexes = vec!["invalid index # / \\name with spaces".to_string()]; - let error = client.update_key(key).await.unwrap_err(); - - assert!(matches!( - error, - Error::MeilisearchError { - error_code: ErrorCode::InvalidApiKeyIndexes, - error_type: ErrorType::InvalidRequest, - .. - } - )); - */ - - // ==> executing the action without enough right - let no_right_key = KeyBuilder::new(&description); - let no_right_key = client.create_key(no_right_key).await.unwrap(); - - // backup the master key for cleanup at the end of the test - let master_client = client.clone(); - client.api_key = Arc::new(no_right_key.key.clone()); - - let error = client.update_key(key).await.unwrap_err(); - - assert!(matches!( - error, - Error::Meilisearch(MeilisearchError { - error_code: ErrorCode::InvalidApiKey, - error_type: ErrorType::Auth, - .. - }) - )); - - // cleanup - master_client.delete_key(&*client.api_key).await.unwrap(); - } - #[meilisearch_test] async fn test_get_index(client: Client, index_uid: String) -> Result<(), Error> { let task = client.create_index(&index_uid, None).await?; diff --git a/src/key.rs b/src/key.rs index 56376bb5..ab0d6712 100644 --- a/src/key.rs +++ b/src/key.rs @@ -6,20 +6,23 @@ use crate::{client::Client, errors::Error}; /// Represent a [meilisearch key](https://docs.meilisearch.com/reference/api/keys.html#returned-fields) /// You can get a [Key] from the [Client::get_key] method. /// Or you can create a [Key] with the [KeyBuilder::create] or [Client::create_key] methods. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct Key { #[serde(skip_serializing_if = "Vec::is_empty")] pub actions: Vec, #[serde(skip_serializing, with = "time::serde::rfc3339")] pub created_at: OffsetDateTime, - pub description: String, + pub description: Option, + pub name: Option, #[serde(with = "time::serde::rfc3339::option")] pub expires_at: Option, #[serde(skip_serializing_if = "Vec::is_empty")] pub indexes: Vec, #[serde(skip_serializing)] pub key: String, + #[serde(skip_serializing)] + pub uid: String, #[serde(skip_serializing, with = "time::serde::rfc3339")] pub updated_at: OffsetDateTime, } @@ -37,23 +40,23 @@ impl Key { /// # /// # futures::executor::block_on(async move { /// # let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); - /// - /// let mut key = KeyBuilder::new("My little lovely test key") + /// let description = "My not so little lovely test key".to_string(); + /// let mut key = KeyBuilder::new() /// .with_action(Action::DocumentsAdd) /// .with_index("*") - /// .create(&client).await.unwrap(); + /// .with_description(&description) + /// .execute(&client).await.unwrap(); /// - /// key.with_description("My not so little lovely test key"); - /// # assert_eq!(key.description, "My not so little lovely test key".to_string()); + /// # assert_eq!(key.description, Some(description)); /// # client.delete_key(key).await.unwrap(); /// # }); /// ``` pub fn with_description(&mut self, desc: impl AsRef) -> &mut Self { - self.description = desc.as_ref().to_string(); + self.description = Some(desc.as_ref().to_string()); self } - /// Add a set of actions the [Key] will be able to execute. + /// Update the name of the key. /// /// # Example /// @@ -65,179 +68,323 @@ impl Key { /// # /// # futures::executor::block_on(async move { /// # let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); - /// - /// let mut key = KeyBuilder::new("My little lovely test key") + /// let name = "lovely key".to_string(); + /// let mut key = KeyBuilder::new() /// .with_action(Action::DocumentsAdd) /// .with_index("*") - /// .create(&client).await.unwrap(); + /// .execute(&client).await.unwrap(); /// - /// key.with_actions([Action::DocumentsGet, Action::DocumentsDelete]); + /// key.with_name(&name); + /// # assert_eq!(key.name, Some(name)); /// # client.delete_key(key).await.unwrap(); /// # }); /// ``` - pub fn with_actions(&mut self, actions: impl IntoIterator) -> &mut Self { - self.actions.extend(actions); + pub fn with_name(&mut self, desc: impl AsRef) -> &mut Self { + self.name = Some(desc.as_ref().to_string()); self } - /// Add one action the [Key] will be able to execute. + /// Update the [Key]. /// /// # Example /// /// ``` - /// # use meilisearch_sdk::{key::KeyBuilder, key::Action, client::Client}; + /// # use meilisearch_sdk::{key::KeyBuilder, client::Client}; /// # /// # let MEILISEARCH_HOST = option_env!("MEILISEARCH_HOST").unwrap_or("http://localhost:7700"); /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey"); /// # /// # futures::executor::block_on(async move { - /// # let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); - /// - /// let mut key = KeyBuilder::new("My little lovely test key") - /// .with_action(Action::DocumentsAdd) - /// .with_index("*") - /// .create(&client).await.unwrap(); + /// let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); + /// let mut key = KeyBuilder::new() + /// .execute(&client).await.unwrap(); + /// let description = "My not so little lovely test key".to_string(); + /// key.with_description(&description); + /// let key = key.update(&client).await.unwrap(); /// - /// key.with_action(Action::DocumentsGet); + /// # assert_eq!(key.description, Some(description)); /// # client.delete_key(key).await.unwrap(); /// # }); /// ``` - pub fn with_action(&mut self, action: Action) -> &mut Self { - self.actions.push(action); - self + pub async fn update(&self, client: &Client) -> Result { + // only send description and name + let mut key_update = KeyUpdater::new(self); + + if let Some(ref description) = self.description { + key_update.with_description(description); + } + if let Some(ref name) = self.name { + key_update.with_name(name); + } + + key_update.execute(client).await } - /// Update the expiration date of the [Key]. + /// Delete the [Key]. /// /// # Example /// /// ``` - /// # use meilisearch_sdk::{key::KeyBuilder, key::Action, client::Client}; - /// use time::{OffsetDateTime, Duration}; + /// # use meilisearch_sdk::{key::KeyBuilder, client::Client}; /// # /// # let MEILISEARCH_HOST = option_env!("MEILISEARCH_HOST").unwrap_or("http://localhost:7700"); /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey"); /// # /// # futures::executor::block_on(async move { - /// # let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); - /// - /// let mut key = KeyBuilder::new("My little lovely test key") - /// .with_action(Action::DocumentsAdd) - /// .with_index("*") - /// .create(&client).await.unwrap(); + /// let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); + /// let mut key = KeyBuilder::new() + /// .execute(&client).await.unwrap(); /// - /// // update the epiry date of the key to two weeks from now - /// key.with_expires_at(OffsetDateTime::now_utc() + Duration::WEEK * 2); - /// # client.delete_key(key).await.unwrap(); + /// client.delete_key(key).await.unwrap(); /// # }); /// ``` - pub fn with_expires_at(&mut self, expires_at: OffsetDateTime) -> &mut Self { - self.expires_at = Some(expires_at); + pub async fn delete(&self, client: &Client) -> Result<(), Error> { + client.delete_key(self).await + } +} + +impl AsRef for Key { + fn as_ref(&self) -> &str { + &self.key + } +} + +impl AsRef for Key { + fn as_ref(&self) -> &Key { self } +} - /// Update the indexes the [Key] can manage. +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct KeyUpdater { + pub description: Option, + pub name: Option, + #[serde(skip_serializing)] + pub key: String, +} + +impl KeyUpdater { + pub fn new(key_or_uid: impl AsRef) -> KeyUpdater { + KeyUpdater { + description: None, + name: None, + key: key_or_uid.as_ref().to_string(), + } + } + + /// Update the description of the key. /// /// # Example /// /// ``` - /// # use meilisearch_sdk::{key::KeyBuilder, key::Action, client::Client}; + /// # use meilisearch_sdk::{key::KeyBuilder, key::Action, client::Client, key::KeyUpdater}; /// # /// # let MEILISEARCH_HOST = option_env!("MEILISEARCH_HOST").unwrap_or("http://localhost:7700"); /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey"); /// # /// # futures::executor::block_on(async move { /// # let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); - /// - /// let mut key = KeyBuilder::new("My little lovely test key") - /// .with_action(Action::DocumentsAdd) - /// .with_index("*") - /// .create(&client).await.unwrap(); - /// - /// key.with_indexes(vec!["test", "movies"]); - /// # client.delete_key(key).await.unwrap(); + /// let mut new_key = KeyBuilder::new() + /// .execute(&client) + /// .await + /// .unwrap(); + /// + /// let description = "My not so little lovely test key".to_string(); + /// let mut key_update = KeyUpdater::new(new_key) + /// .with_description(&description) + /// .execute(&client) + /// .await + /// .unwrap(); + /// + /// # assert_eq!(key_update.description, Some(description)); + /// # client.delete_key(key_update).await.unwrap(); /// # }); /// ``` - pub fn with_indexes( - &mut self, - indexes: impl IntoIterator>, - ) -> &mut Self { - self.indexes = indexes - .into_iter() - .map(|index| index.as_ref().to_string()) - .collect(); + pub fn with_description(&mut self, desc: impl AsRef) -> &mut Self { + self.description = Some(desc.as_ref().to_string()); self } - /// Add one index the [Key] can manage. + /// Update the name of the key. /// /// # Example /// /// ``` - /// # use meilisearch_sdk::{key::KeyBuilder, key::Action, client::Client}; + /// # use meilisearch_sdk::{key::KeyBuilder, key::Action, client::Client, key::KeyUpdater}; /// # /// # let MEILISEARCH_HOST = option_env!("MEILISEARCH_HOST").unwrap_or("http://localhost:7700"); /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey"); /// # /// # futures::executor::block_on(async move { /// # let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); + /// let mut new_key = KeyBuilder::new() + /// .execute(&client) + /// .await + /// .unwrap(); /// - /// let mut key = KeyBuilder::new("My little lovely test key") - /// .with_action(Action::DocumentsAdd) - /// .with_index("*") - /// .create(&client).await.unwrap(); + /// let name = "lovely key".to_string(); /// - /// key.with_index("test"); - /// # client.delete_key(key).await.unwrap(); + /// let mut key_update = KeyUpdater::new(new_key) + /// .with_name(&name) + /// .execute(&client) + /// .await + /// .unwrap(); + /// + /// # assert_eq!(key_update.name, Some(name)); + /// # client.delete_key(key_update).await.unwrap(); /// # }); /// ``` - pub fn with_index(&mut self, index: impl AsRef) -> &mut Self { - self.indexes.push(index.as_ref().to_string()); + pub fn with_name(&mut self, desc: impl AsRef) -> &mut Self { + self.name = Some(desc.as_ref().to_string()); self } - /// Update the [Key]. + /// Update a [Key] using the [KeyUpdater]. /// /// # Example /// /// ``` - /// # use meilisearch_sdk::{key::KeyBuilder, client::Client}; + /// # use meilisearch_sdk::{key::KeyBuilder, key::KeyUpdater, client::Client}; /// # /// # let MEILISEARCH_HOST = option_env!("MEILISEARCH_HOST").unwrap_or("http://localhost:7700"); /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey"); /// # /// # futures::executor::block_on(async move { /// let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); - /// let mut key = KeyBuilder::new("My little lovely test key") - /// .create(&client).await.unwrap(); - /// - /// # assert_eq!(key.description, "My little lovely test key"); - /// - /// key.with_description("My not so little lovely test key"); - /// let key = key.update(&client).await.unwrap(); + /// let description = "My little lovely test key".to_string(); + /// let key = KeyBuilder::new() + /// .execute(&client).await.unwrap(); /// - /// # assert_eq!(key.description, "My not so little lovely test key".to_string()); + /// let mut key_update = KeyUpdater::new(&key.key); + /// key_update.with_description(&description).execute(&client).await; /// + /// assert_eq!(key_update.description, Some(description)); /// # client.delete_key(key).await.unwrap(); /// # }); /// ``` - pub async fn update(&self, client: &Client) -> Result { + pub async fn execute(&self, client: &Client) -> Result { client.update_key(self).await } } -impl AsRef for Key { +impl AsRef for KeyUpdater { fn as_ref(&self) -> &str { &self.key } } -impl AsRef for Key { - fn as_ref(&self) -> &Key { +impl AsRef for KeyUpdater { + fn as_ref(&self) -> &KeyUpdater { self } } +#[derive(Debug, Serialize, Clone, Default)] +#[serde(rename_all = "camelCase")] +pub struct KeysQuery { + /// The number of documents to skip. + /// If the value of the parameter `offset` is `n`, the `n` first documents (ordered by relevance) will not be returned. + /// This is helpful for pagination. + /// + /// Example: If you want to skip the first document, set offset to `1`. + #[serde(skip_serializing_if = "Option::is_none")] + pub offset: Option, + /// The maximum number of documents returned. + /// If the value of the parameter `limit` is `n`, there will never be more than `n` documents in the response. + /// This is helpful for pagination. + /// + /// Example: If you don't want to get more than two documents, set limit to `2`. + /// Default: `20` + #[serde(skip_serializing_if = "Option::is_none")] + pub limit: Option, +} + +impl KeysQuery { + /// Create a [KeysQuery] with only a description. + /// + /// # Example + /// + /// ``` + /// # use meilisearch_sdk::{key::KeysQuery}; + /// let builder = KeysQuery::new(); + /// ``` + pub fn new() -> KeysQuery { + Self::default() + } + + /// Specify the offset. + /// + /// # Example + /// + /// ``` + /// # use meilisearch_sdk::{key::KeysQuery, key::Action, client::Client}; + /// # + /// # let MEILISEARCH_HOST = option_env!("MEILISEARCH_HOST").unwrap_or("http://localhost:7700"); + /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey"); + /// # + /// # futures::executor::block_on(async move { + /// # let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); + /// let mut keys = KeysQuery::new() + /// .with_offset(1) + /// .execute(&client).await.unwrap(); + /// + /// # assert_eq!(keys.results.len(), 1); + /// # }); + /// ``` + pub fn with_offset(&mut self, offset: usize) -> &mut KeysQuery { + self.offset = Some(offset); + self + } + + /// Specify the maximum number of keys to return. + /// + /// # Example + /// + /// ``` + /// # use meilisearch_sdk::{key::KeysQuery, key::Action, client::Client}; + /// # + /// # let MEILISEARCH_HOST = option_env!("MEILISEARCH_HOST").unwrap_or("http://localhost:7700"); + /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey"); + /// # + /// # futures::executor::block_on(async move { + /// # let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); + /// let mut keys = KeysQuery::new() + /// .with_limit(1) + /// .execute(&client).await.unwrap(); + /// + /// # assert_eq!(keys.results.len(), 1); + /// # }); + /// ``` + pub fn with_limit(&mut self, limit: usize) -> &mut KeysQuery { + self.limit = Some(limit); + self + } + + /// Get [Key]'s. + /// + /// # Example + /// + /// ``` + /// # use meilisearch_sdk::{key::KeysQuery, key::Action, client::Client}; + /// # + /// # let MEILISEARCH_HOST = option_env!("MEILISEARCH_HOST").unwrap_or("http://localhost:7700"); + /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey"); + /// # + /// # futures::executor::block_on(async move { + /// # let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); + /// let mut keys = KeysQuery::new() + /// .with_limit(1) + /// .execute(&client).await.unwrap(); + /// + /// # assert_eq!(keys.results.len(), 1); + /// # }); + /// ``` + pub async fn execute(&self, client: &Client) -> Result { + client.get_keys_with(self).await + } +} + /// The [KeyBuilder] is an analog to the [Key] type but without all the fields managed by Meilisearch. /// It's used to create [Key]. /// @@ -251,42 +398,41 @@ impl AsRef for Key { /// # /// # futures::executor::block_on(async move { /// let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); +/// let description = "My little lovely test key".to_string(); +/// let key = KeyBuilder::new() +/// .with_description(&description) +/// .execute(&client).await.unwrap(); /// -/// let key = KeyBuilder::new("My little lovely test key") -/// .with_action(Action::DocumentsAdd) -/// .with_index("*") -/// .create(&client).await.unwrap(); -/// -/// assert_eq!(key.description, "My little lovely test key"); +/// # assert_eq!(key.description, Some(description)); /// # client.delete_key(key).await.unwrap(); /// # }); /// ``` -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Default)] #[serde(rename_all = "camelCase")] pub struct KeyBuilder { pub actions: Vec, - pub description: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub uid: Option, #[serde(with = "time::serde::rfc3339::option")] pub expires_at: Option, pub indexes: Vec, } impl KeyBuilder { - /// Create a [KeyBuilder] with only a description. + /// Create a [KeyBuilder]. /// /// # Example /// /// ``` /// # use meilisearch_sdk::{key::KeyBuilder}; - /// let builder = KeyBuilder::new("My little lovely test key"); - /// ``` - pub fn new(description: impl AsRef) -> KeyBuilder { - Self { - actions: Vec::new(), - description: description.as_ref().to_string(), - expires_at: None, - indexes: Vec::new(), - } + /// let builder = KeyBuilder::new(); + /// ``` + pub fn new() -> KeyBuilder { + Self::default() } /// Declare a set of actions the [Key] will be able to execute. @@ -295,7 +441,7 @@ impl KeyBuilder { /// /// ``` /// # use meilisearch_sdk::key::{KeyBuilder, Action}; - /// let mut builder = KeyBuilder::new("My little lovely test key"); + /// let mut builder = KeyBuilder::new(); /// builder.with_actions(vec![Action::Search, Action::DocumentsAdd]); /// ``` pub fn with_actions(&mut self, actions: impl IntoIterator) -> &mut Self { @@ -309,7 +455,7 @@ impl KeyBuilder { /// /// ``` /// # use meilisearch_sdk::key::{KeyBuilder, Action}; - /// let mut builder = KeyBuilder::new("My little lovely test key"); + /// let mut builder = KeyBuilder::new(); /// builder.with_action(Action::DocumentsAdd); /// ``` pub fn with_action(&mut self, action: Action) -> &mut Self { @@ -324,7 +470,7 @@ impl KeyBuilder { /// ``` /// # use meilisearch_sdk::{key::KeyBuilder}; /// use time::{OffsetDateTime, Duration}; - /// let mut builder = KeyBuilder::new("My little lovely test key"); + /// let mut builder = KeyBuilder::new(); /// // create a key that expires in two weeks from now /// builder.with_expires_at(OffsetDateTime::now_utc() + Duration::WEEK * 2); /// ``` @@ -338,9 +484,22 @@ impl KeyBuilder { /// # Example /// /// ``` - /// # use meilisearch_sdk::{key::KeyBuilder}; - /// let mut builder = KeyBuilder::new("My little lovely test key"); - /// builder.with_indexes(vec!["test", "movies"]); + /// # use meilisearch_sdk::{key::KeyBuilder, client::Client}; + /// # + /// # let MEILISEARCH_HOST = option_env!("MEILISEARCH_HOST").unwrap_or("http://localhost:7700"); + /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey"); + /// # + /// # futures::executor::block_on(async move { + /// # let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); + /// let mut key = KeyBuilder::new() + /// .with_indexes(vec!["test", "movies"]) + /// .execute(&client) + /// .await + /// .unwrap(); + /// + /// assert_eq!(vec!["test", "movies"], key.indexes); + /// # client.delete_key(key).await.unwrap(); + /// # }); /// ``` pub fn with_indexes( &mut self, @@ -359,7 +518,7 @@ impl KeyBuilder { /// /// ``` /// # use meilisearch_sdk::{key::KeyBuilder}; - /// let mut builder = KeyBuilder::new("My little lovely test key"); + /// let mut builder = KeyBuilder::new(); /// builder.with_index("test"); /// ``` pub fn with_index(&mut self, index: impl AsRef) -> &mut Self { @@ -367,6 +526,88 @@ impl KeyBuilder { self } + /// Add a description to the key. + /// + /// # Example + /// + /// ``` + /// # use meilisearch_sdk::{key::KeyBuilder, key::Action, client::Client}; + /// # + /// # let MEILISEARCH_HOST = option_env!("MEILISEARCH_HOST").unwrap_or("http://localhost:7700"); + /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey"); + /// # + /// # futures::executor::block_on(async move { + /// # let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); + /// let description = "My not so little lovely test key".to_string(); + /// + /// let mut key = KeyBuilder::new() + /// .with_description(&description) + /// .execute(&client).await.unwrap(); + /// + /// # assert_eq!(key.description, Some(description)); + /// # client.delete_key(key).await.unwrap(); + /// # }); + /// ``` + pub fn with_description(&mut self, desc: impl AsRef) -> &mut Self { + self.description = Some(desc.as_ref().to_string()); + self + } + + /// Add a name to the key. + /// + /// # Example + /// + /// ``` + /// # use meilisearch_sdk::{key::KeyBuilder, key::Action, client::Client}; + /// # + /// # let MEILISEARCH_HOST = option_env!("MEILISEARCH_HOST").unwrap_or("http://localhost:7700"); + /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey"); + /// # + /// # futures::executor::block_on(async move { + /// # let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); + /// let name = "lovely key".to_string(); + /// + /// let mut key = KeyBuilder::new() + /// .with_name(&name) + /// .execute(&client).await.unwrap(); + /// + /// # assert_eq!(key.name, Some(name)); + /// # client.delete_key(key).await.unwrap(); + /// # }); + /// ``` + pub fn with_name(&mut self, desc: impl AsRef) -> &mut Self { + self.name = Some(desc.as_ref().to_string()); + self + } + + /// Add an uid to the key. + /// + /// # Example + /// + /// ``` + /// # use meilisearch_sdk::{key::KeyBuilder, key::Action, client::Client}; + /// # + /// # let MEILISEARCH_HOST = option_env!("MEILISEARCH_HOST").unwrap_or("http://localhost:7700"); + /// # let MEILISEARCH_API_KEY = option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey"); + /// # + /// # futures::executor::block_on(async move { + /// # let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); + /// let uid = "93bcd7fb-2196-4fd9-acb7-3fca8a96e78f".to_string(); + /// + /// let mut key = KeyBuilder::new() + /// .with_uid(&uid) + /// .execute(&client).await.unwrap(); + /// + /// + /// # assert_eq!(key.uid, uid); + /// # client.delete_key(key).await.unwrap(); + /// # }); + /// ``` + pub fn with_uid(&mut self, desc: impl AsRef) -> &mut Self { + self.uid = Some(desc.as_ref().to_string()); + self + } + /// Create a [Key] from the builder. /// /// # Example @@ -379,14 +620,16 @@ impl KeyBuilder { /// # /// # futures::executor::block_on(async move { /// let client = Client::new(MEILISEARCH_HOST, MEILISEARCH_API_KEY); - /// let key = KeyBuilder::new("My little lovely test key") - /// .create(&client).await.unwrap(); + /// let description = "My little lovely test key".to_string(); + /// let key = KeyBuilder::new() + /// .with_description(&description) + /// .execute(&client).await.unwrap(); /// - /// assert_eq!(key.description, "My little lovely test key"); + /// # assert_eq!(key.description, Some(description)); /// # client.delete_key(key).await.unwrap(); /// # }); /// ``` - pub async fn create(&self, client: &Client) -> Result { + pub async fn execute(&self, client: &Client) -> Result { client.create_key(self).await } } @@ -447,4 +690,23 @@ pub enum Action { /// Provides access to the [get Meilisearch version](https://docs.meilisearch.com/reference/api/version.md#get-version-of-meilisearch) endpoint. #[serde(rename = "version")] Version, + /// Provides access to the [get Key](https://docs.meilisearch.com/reference/api/keys.html#get-one-key) and [get Keys](https://docs.meilisearch.com/reference/api/keys.html#get-all-keys) endpoints. + #[serde(rename = "keys.get")] + KeyGet, + /// Provides access to the [create key](https://docs.meilisearch.com/reference/api/keys.html#create-a-key) endpoint. + #[serde(rename = "keys.create")] + KeyCreate, + /// Provides access to the [update key](https://docs.meilisearch.com/reference/api/keys.html#update-a-key) endpoint. + #[serde(rename = "keys.update")] + KeyUpdate, + /// Provides access to the [delete key](https://docs.meilisearch.com/reference/api/keys.html#delete-a-key) endpoint. + #[serde(rename = "keys.delete")] + KeyDelete, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct KeysResults { + pub results: Vec, + pub limit: u32, + pub offset: u32, } diff --git a/src/search.rs b/src/search.rs index 954bedaf..db93f4dd 100644 --- a/src/search.rs +++ b/src/search.rs @@ -734,10 +734,10 @@ mod tests { setup_test_index(&client, &index).await?; let meilisearch_host = option_env!("MEILISEARCH_HOST").unwrap_or("http://localhost:7700"); - let key = KeyBuilder::new("key for generate_tenant_token test") + let key = KeyBuilder::new() .with_action(Action::All) .with_index("*") - .create(&client) + .execute(&client) .await .unwrap(); let allowed_client = Client::new(meilisearch_host, key.key);