Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .code-samples.meilisearch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -690,15 +690,15 @@ get_all_keys_1: |-
create_a_key_1: |-
let mut key_options = KeyBuilder::new("Add documents: Products API key");
key_options.with_action(Action::DocumentsAdd)
.with_expires_at("2042-04-02T00:42:42Z")
.with_expires_at(time::macros::datetime!(2042 - 04 - 02 00:42:42 UTC))
.with_index("products");
let new_key = client.create_key(key_options).await.unwrap();
update_a_key_1: |-
let key = client.get_key("d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4").await.unwrap();
key.description = "Manage documents: Products/Reviews API key".to_string();
key.actions = vec![Action::DocumentsAdd, Action::DocumentsDelete];
key.indexes = vec!["products".to_string(), "reviews".to_string()];
key.expires_at = Some("2042-04-02T00:42:42Z".to_string());
key.expires_at = Some(time::macros::datetime!(2042 - 04 - 02 00:42:42 UTC));
let updated_key = client.update_key(&key);
delete_a_key_1: |-
let key = client.get_key("d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4").await.unwrap();
Expand All @@ -718,7 +718,7 @@ security_guide_create_key_1: |-
let client = Client::new("http://localhost:7700", "masterKey");
let mut key_options = KeyBuilder::new("Search patient records key");
key_options.with_action(Action::Search)
.with_expires_at("2023-01-01T00:00:00Z")
.with_expires_at(time::macros::datetime!(2023 - 01 - 01 00:00:00 UTC))
.with_index("patient_medical_records");
let new_key = client.create_key(key_options).await.unwrap();
security_guide_list_keys_1: |-
Expand Down
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ repository = "https://github.com/meilisearch/meilisearch-sdk"

[dependencies]
async-trait = "0.1.51"
serde_json = "1.0"
iso8601-duration = "0.1.0"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the newbie here 😅

I think I get the idea, but just to be sure, the users of the rust-sdk don't need to have these time-handling libraries on their apps?
In other words, to be able to use some function they need to have the lib, in order to have the right types to pass to the function?

Copy link
Contributor Author

@irevoire irevoire Feb 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The iso8601-duration crate is never exposed to the end-user, but sadly the time crate will be needed if someone wants to manipulate API keys.

For the context, there was a lot of TODO: use chrono (which is one of the biggest time crates in the rust ecosystem).
And there was this comment asking for a real « time » type instead of String in the SDK: meilisearch/integration-guides#121 (comment)

And the reason why we’re not using chrono is here: #226
There is a bunch (2 or 3, I think) of CVE on chrono that are not fixed because the crate is not really maintained anymore.
But for our use case, chrono was just a wrapper around time (which is maintained and fixed all of the CVE encountered in chrono), so I decided to import this one instead of chrono.
Also, time is way smaller than chrono, and you can configure a lot of features depending on your needs.

But yes, to conclude, a user of the meilisearch-rust sdk can do most of the thing without any problem, but if he wants to use inspect, create or update anything related to the OffsetDateTime, he’ll need to import time in his crate.

log = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
time = { version = "0.3.7", features = ["serde-well-known", "formatting", "parsing"] }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
futures = "0.3"
Expand Down
79 changes: 32 additions & 47 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ impl Client {
/// # futures::executor::block_on(async move {
/// // create the client
/// let client = Client::new("http://localhost:7700", "masterKey");
/// # let index = client.create_index("movies", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
/// # let index = client.create_index("get_index", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
///
/// // get the index named "movies"
/// let movies = client.get_index("movies").await.unwrap();
/// assert_eq!(movies.as_ref(), "movies");
/// // get the index named "get_index"
/// let index = client.get_index("get_index").await.unwrap();
/// assert_eq!(index.as_ref(), "get_index");
/// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
/// # });
/// ```
Expand All @@ -124,11 +124,11 @@ impl Client {
/// # futures::executor::block_on(async move {
/// // create the client
/// let client = Client::new("http://localhost:7700", "masterKey");
/// # let index = client.create_index("movies", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
/// # let index = client.create_index("get_raw_index", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
///
/// // get the index named "movies"
/// let movies = client.get_raw_index("movies").await.unwrap();
/// assert_eq!(movies.uid, "movies");
/// // get the index named "get_raw_index"
/// let raw_index = client.get_raw_index("get_raw_index").await.unwrap();
/// assert_eq!(raw_index.uid, "get_raw_index");
/// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
/// # });
/// ```
Expand Down Expand Up @@ -158,15 +158,15 @@ impl Client {
/// let client = Client::new("http://localhost:7700", "masterKey");
///
/// // Create a new index called movies and access it
/// let task = client.create_index("movies", None).await.unwrap();
/// let task = client.create_index("create_index", None).await.unwrap();
///
/// // Wait for the task to complete
/// let task = task.wait_for_completion(&client, None, None).await.unwrap();
///
/// // Try to get the inner index if the task succeeded
/// let index = task.try_make_index(&client).unwrap();
///
/// assert_eq!(index.as_ref(), "movies");
/// assert_eq!(index.as_ref(), "create_index");
/// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
/// # });
/// ```
Expand Down Expand Up @@ -634,6 +634,7 @@ mod tests {
key::{Action, KeyBuilder},
};
use meilisearch_test_macro::meilisearch_test;
use time::OffsetDateTime;

#[meilisearch_test]
async fn test_get_keys(client: Client) {
Expand Down Expand Up @@ -705,15 +706,20 @@ mod tests {

#[meilisearch_test]
async fn test_create_key(client: Client, description: String) {
let expires_at = OffsetDateTime::now_utc() + time::Duration::HOUR;
let mut key = KeyBuilder::new(description.clone());
key.with_action(Action::DocumentsAdd)
.with_expires_at("3022-02-09T01:32:46Z")
.with_expires_at(expires_at.clone())
.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.expires_at, Some("3022-02-09T01:32:46Z".to_string()));
// 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 keys = client.get_keys().await.unwrap();
Expand All @@ -722,9 +728,10 @@ mod tests {

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,
Some("3022-02-09T01:32:46Z".to_string())
remote_key.expires_at.unwrap().unix_timestamp(),
expires_at.unix_timestamp()
);
assert_eq!(remote_key.indexes, vec!["*".to_string()]);

Expand All @@ -749,20 +756,6 @@ mod tests {
));
*/

// ==> Invalid expires_at
let mut key = KeyBuilder::new(&description);
key.with_expires_at("That’s totally not a date");
let error = client.create_key(key).await.unwrap_err();

assert!(matches!(
error,
Error::Meilisearch(MeilisearchError {
error_code: ErrorCode::InvalidApiKeyExpiresAt,
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();
Expand All @@ -789,18 +782,23 @@ 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 = client.create_key(key).await.unwrap();

key.actions = vec![Action::DocumentsAdd];
key.expires_at = Some("3022-02-09T01:32:46Z".to_string());
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);
assert_eq!(key.expires_at, Some("3022-02-09T01:32:46Z".to_string()));
// 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 keys = client.get_keys().await.unwrap();
Expand All @@ -809,9 +807,10 @@ mod tests {

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,
Some("3022-02-09T01:32:46Z".to_string())
remote_key.expires_at.unwrap().unix_timestamp(),
expires_at.unix_timestamp()
);
assert_eq!(remote_key.indexes, vec!["*".to_string()]);

Expand All @@ -821,7 +820,7 @@ mod tests {
#[meilisearch_test]
async fn test_error_update_key(mut client: Client, description: String) {
let key = KeyBuilder::new(description.clone());
let mut key = client.create_key(key).await.unwrap();
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
Expand All @@ -838,20 +837,6 @@ mod tests {
));
*/

// ==> Invalid expires_at
key.expires_at = Some("That’s totally not a date".to_string());
let error = client.update_key(&key).await.unwrap_err();

assert!(matches!(
error,
Error::Meilisearch(MeilisearchError {
error_code: ErrorCode::InvalidApiKeyExpiresAt,
error_type: ErrorType::InvalidRequest,
..
})
));
key.expires_at = None;

// ==> 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();
Expand Down
7 changes: 5 additions & 2 deletions src/indexes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ use crate::{client::Client, document::*, errors::Error, request::*, search::*, t
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::json;
use std::{collections::HashMap, fmt::Display, time::Duration};
use time::OffsetDateTime;

#[derive(Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct JsonIndex {
pub uid: String,
pub primaryKey: Option<String>,
pub createdAt: String, // TODO: use a chrono date
pub updatedAt: String, // TODO: use a chrono date
#[serde(with = "time::serde::rfc3339")]
pub createdAt: OffsetDateTime,
#[serde(with = "time::serde::rfc3339")]
pub updatedAt: OffsetDateTime,
}

impl JsonIndex {
Expand Down
24 changes: 14 additions & 10 deletions src/key.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;

use crate::{client::Client, errors::Error};

Expand All @@ -10,17 +11,17 @@ use crate::{client::Client, errors::Error};
pub struct Key {
#[serde(skip_serializing_if = "Vec::is_empty")]
pub actions: Vec<Action>,
#[serde(skip_serializing)]
pub created_at: String, // TODO: use a chrono date
#[serde(skip_serializing, with = "time::serde::rfc3339")]
pub created_at: OffsetDateTime,
pub description: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub expires_at: Option<String>, // TODO: use a chrono date
#[serde(with = "time::serde::rfc3339::option")]
pub expires_at: Option<OffsetDateTime>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub indexes: Vec<String>,
#[serde(skip_serializing)]
pub key: String,
#[serde(skip_serializing)]
pub updated_at: String, // TODO: use a chrono date
#[serde(skip_serializing, with = "time::serde::rfc3339")]
pub updated_at: OffsetDateTime,
}

impl AsRef<str> for Key {
Expand Down Expand Up @@ -59,7 +60,8 @@ impl AsRef<Key> for Key {
pub struct KeyBuilder {
pub actions: Vec<Action>,
pub description: String,
pub expires_at: Option<String>, // TODO: use a chrono date
#[serde(with = "time::serde::rfc3339::option")]
pub expires_at: Option<OffsetDateTime>,
pub indexes: Vec<String>,
}

Expand Down Expand Up @@ -115,11 +117,13 @@ impl KeyBuilder {
///
/// ```
/// # use meilisearch_sdk::{key::KeyBuilder};
/// use time::{OffsetDateTime, Duration};
/// let mut builder = KeyBuilder::new("My little lovely test key");
/// builder.with_expires_at("3022-02-09T10:35:58Z".to_string());
/// // create a key that expires in two weeks from now
/// builder.with_expires_at(OffsetDateTime::now_utc() + Duration::WEEK * 2);
/// ```
pub fn with_expires_at(&mut self, expires_at: impl AsRef<str>) -> &mut Self {
self.expires_at = Some(expires_at.as_ref().to_string());
pub fn with_expires_at(&mut self, expires_at: OffsetDateTime) -> &mut Self {
self.expires_at = Some(expires_at);
self
}

Expand Down
39 changes: 31 additions & 8 deletions src/tasks.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use serde::Deserialize;
use serde::{Deserialize, Deserializer};
use std::time::Duration;
use time::OffsetDateTime;

use crate::{
client::Client, errors::Error, errors::MeilisearchError, indexes::Index, settings::Settings,
Expand Down Expand Up @@ -57,13 +58,26 @@ impl AsRef<u64> for FailedTask {
}
}

fn deserialize_duration<'de, D>(deserializer: D) -> Result<std::time::Duration, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let iso_duration = iso8601_duration::Duration::parse(&s).map_err(serde::de::Error::custom)?;
Ok(iso_duration.to_std())
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ProcessedTask {
pub duration: String, // TODO deserialize to Duration
pub enqueued_at: String, // TODO deserialize to datetime
pub started_at: String, // TODO deserialize to datetime
pub finished_at: String, // TODO deserialize to datetime
#[serde(deserialize_with = "deserialize_duration")]
pub duration: Duration,
#[serde(with = "time::serde::rfc3339")]
pub enqueued_at: OffsetDateTime,
#[serde(with = "time::serde::rfc3339")]
pub started_at: OffsetDateTime,
#[serde(with = "time::serde::rfc3339")]
pub finished_at: OffsetDateTime,
pub index_uid: String,
#[serde(flatten)]
pub update_type: TaskType,
Expand All @@ -79,7 +93,8 @@ impl AsRef<u64> for ProcessedTask {
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EnqueuedTask {
pub enqueued_at: String, // TODO deserialize to datetime
#[serde(with = "time::serde::rfc3339")]
pub enqueued_at: OffsetDateTime,
pub index_uid: String,
#[serde(flatten)]
pub update_type: TaskType,
Expand Down Expand Up @@ -392,7 +407,13 @@ mod test {
}

#[test]
fn test_deserialize_enqueued_task() {
fn test_deserialize_task() {
let datetime = OffsetDateTime::parse(
"2022-02-03T13:02:38.369634Z",
&::time::format_description::well_known::Rfc3339,
)
.unwrap();

let task: Task = serde_json::from_str(
r#"
{
Expand All @@ -415,7 +436,7 @@ mod test {
uid: 12,
}
}
if enqueued_at == "2022-02-03T13:02:38.369634Z" && index_uid == "mieli"));
if enqueued_at == datetime && index_uid == "mieli"));

let task: Task = serde_json::from_str(
r#"
Expand Down Expand Up @@ -482,9 +503,11 @@ mod test {
})
},
uid: 14,
duration,
..
}
}
if duration == Duration::from_secs_f32(10.848957061)
));
}

Expand Down