Skip to content

Commit 485f19a

Browse files
bors[bot]irevoire
andauthored
Merge #237
237: Put type on timestamp and duration r=brunoocasali a=irevoire Parse the Duration and timestamp with the `iso8601-duration` and the `time` crate Co-authored-by: Irevoire <tamo@meilisearch.com>
2 parents 8ca3923 + d884431 commit 485f19a

File tree

6 files changed

+88
-71
lines changed

6 files changed

+88
-71
lines changed

.code-samples.meilisearch.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -690,15 +690,15 @@ get_all_keys_1: |-
690690
create_a_key_1: |-
691691
let mut key_options = KeyBuilder::new("Add documents: Products API key");
692692
key_options.with_action(Action::DocumentsAdd)
693-
.with_expires_at("2042-04-02T00:42:42Z")
693+
.with_expires_at(time::macros::datetime!(2042 - 04 - 02 00:42:42 UTC))
694694
.with_index("products");
695695
let new_key = client.create_key(key_options).await.unwrap();
696696
update_a_key_1: |-
697697
let key = client.get_key("d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4").await.unwrap();
698698
key.description = "Manage documents: Products/Reviews API key".to_string();
699699
key.actions = vec![Action::DocumentsAdd, Action::DocumentsDelete];
700700
key.indexes = vec!["products".to_string(), "reviews".to_string()];
701-
key.expires_at = Some("2042-04-02T00:42:42Z".to_string());
701+
key.expires_at = Some(time::macros::datetime!(2042 - 04 - 02 00:42:42 UTC));
702702
let updated_key = client.update_key(&key);
703703
delete_a_key_1: |-
704704
let key = client.get_key("d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4").await.unwrap();
@@ -718,7 +718,7 @@ security_guide_create_key_1: |-
718718
let client = Client::new("http://localhost:7700", "masterKey");
719719
let mut key_options = KeyBuilder::new("Search patient records key");
720720
key_options.with_action(Action::Search)
721-
.with_expires_at("2023-01-01T00:00:00Z")
721+
.with_expires_at(time::macros::datetime!(2023 - 01 - 01 00:00:00 UTC))
722722
.with_index("patient_medical_records");
723723
let new_key = client.create_key(key_options).await.unwrap();
724724
security_guide_list_keys_1: |-

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ repository = "https://github.com/meilisearch/meilisearch-sdk"
1010

1111
[dependencies]
1212
async-trait = "0.1.51"
13-
serde_json = "1.0"
13+
iso8601-duration = "0.1.0"
1414
log = "0.4"
1515
serde = { version = "1.0", features = ["derive"] }
16+
serde_json = "1.0"
17+
time = { version = "0.3.7", features = ["serde-well-known", "formatting", "parsing"] }
1618

1719
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
1820
futures = "0.3"

src/client.rs

Lines changed: 32 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,11 @@ impl Client {
9999
/// # futures::executor::block_on(async move {
100100
/// // create the client
101101
/// let client = Client::new("http://localhost:7700", "masterKey");
102-
/// # let index = client.create_index("movies", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
102+
/// # let index = client.create_index("get_index", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
103103
///
104-
/// // get the index named "movies"
105-
/// let movies = client.get_index("movies").await.unwrap();
106-
/// assert_eq!(movies.as_ref(), "movies");
104+
/// // get the index named "get_index"
105+
/// let index = client.get_index("get_index").await.unwrap();
106+
/// assert_eq!(index.as_ref(), "get_index");
107107
/// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
108108
/// # });
109109
/// ```
@@ -124,11 +124,11 @@ impl Client {
124124
/// # futures::executor::block_on(async move {
125125
/// // create the client
126126
/// let client = Client::new("http://localhost:7700", "masterKey");
127-
/// # let index = client.create_index("movies", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
127+
/// # let index = client.create_index("get_raw_index", None).await.unwrap().wait_for_completion(&client, None, None).await.unwrap().try_make_index(&client).unwrap();
128128
///
129-
/// // get the index named "movies"
130-
/// let movies = client.get_raw_index("movies").await.unwrap();
131-
/// assert_eq!(movies.uid, "movies");
129+
/// // get the index named "get_raw_index"
130+
/// let raw_index = client.get_raw_index("get_raw_index").await.unwrap();
131+
/// assert_eq!(raw_index.uid, "get_raw_index");
132132
/// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
133133
/// # });
134134
/// ```
@@ -158,15 +158,15 @@ impl Client {
158158
/// let client = Client::new("http://localhost:7700", "masterKey");
159159
///
160160
/// // Create a new index called movies and access it
161-
/// let task = client.create_index("movies", None).await.unwrap();
161+
/// let task = client.create_index("create_index", None).await.unwrap();
162162
///
163163
/// // Wait for the task to complete
164164
/// let task = task.wait_for_completion(&client, None, None).await.unwrap();
165165
///
166166
/// // Try to get the inner index if the task succeeded
167167
/// let index = task.try_make_index(&client).unwrap();
168168
///
169-
/// assert_eq!(index.as_ref(), "movies");
169+
/// assert_eq!(index.as_ref(), "create_index");
170170
/// # index.delete().await.unwrap().wait_for_completion(&client, None, None).await.unwrap();
171171
/// # });
172172
/// ```
@@ -634,6 +634,7 @@ mod tests {
634634
key::{Action, KeyBuilder},
635635
};
636636
use meilisearch_test_macro::meilisearch_test;
637+
use time::OffsetDateTime;
637638

638639
#[meilisearch_test]
639640
async fn test_get_keys(client: Client) {
@@ -705,15 +706,20 @@ mod tests {
705706

706707
#[meilisearch_test]
707708
async fn test_create_key(client: Client, description: String) {
709+
let expires_at = OffsetDateTime::now_utc() + time::Duration::HOUR;
708710
let mut key = KeyBuilder::new(description.clone());
709711
key.with_action(Action::DocumentsAdd)
710-
.with_expires_at("3022-02-09T01:32:46Z")
712+
.with_expires_at(expires_at.clone())
711713
.with_index("*");
712714
let key = client.create_key(key).await.unwrap();
713715

714716
assert_eq!(key.actions, vec![Action::DocumentsAdd]);
715717
assert_eq!(key.description, description);
716-
assert_eq!(key.expires_at, Some("3022-02-09T01:32:46Z".to_string()));
718+
// We can't compare the two timestamp directly because of some nanoseconds imprecision with the floats
719+
assert_eq!(
720+
key.expires_at.unwrap().unix_timestamp(),
721+
expires_at.unix_timestamp()
722+
);
717723
assert_eq!(key.indexes, vec!["*".to_string()]);
718724

719725
let keys = client.get_keys().await.unwrap();
@@ -722,9 +728,10 @@ mod tests {
722728

723729
assert_eq!(remote_key.actions, vec![Action::DocumentsAdd]);
724730
assert_eq!(remote_key.description, description);
731+
// We can't compare the two timestamp directly because of some nanoseconds imprecision with the floats
725732
assert_eq!(
726-
remote_key.expires_at,
727-
Some("3022-02-09T01:32:46Z".to_string())
733+
remote_key.expires_at.unwrap().unix_timestamp(),
734+
expires_at.unix_timestamp()
728735
);
729736
assert_eq!(remote_key.indexes, vec!["*".to_string()]);
730737

@@ -749,20 +756,6 @@ mod tests {
749756
));
750757
*/
751758

752-
// ==> Invalid expires_at
753-
let mut key = KeyBuilder::new(&description);
754-
key.with_expires_at("That’s totally not a date");
755-
let error = client.create_key(key).await.unwrap_err();
756-
757-
assert!(matches!(
758-
error,
759-
Error::Meilisearch(MeilisearchError {
760-
error_code: ErrorCode::InvalidApiKeyExpiresAt,
761-
error_type: ErrorType::InvalidRequest,
762-
..
763-
})
764-
));
765-
766759
// ==> executing the action without enough right
767760
let no_right_key = KeyBuilder::new(&description);
768761
let no_right_key = client.create_key(no_right_key).await.unwrap();
@@ -789,18 +782,23 @@ mod tests {
789782

790783
#[meilisearch_test]
791784
async fn test_update_key(client: Client, description: String) {
785+
let expires_at = OffsetDateTime::now_utc() + time::Duration::HOUR;
792786
let key = KeyBuilder::new(description.clone());
793787
let mut key = client.create_key(key).await.unwrap();
794788

795789
key.actions = vec![Action::DocumentsAdd];
796-
key.expires_at = Some("3022-02-09T01:32:46Z".to_string());
790+
key.expires_at = Some(expires_at);
797791
key.indexes = vec!["*".to_string()];
798792

799793
let key = client.update_key(key).await.unwrap();
800794

801795
assert_eq!(key.actions, vec![Action::DocumentsAdd]);
802796
assert_eq!(key.description, description);
803-
assert_eq!(key.expires_at, Some("3022-02-09T01:32:46Z".to_string()));
797+
// We can't compare the two timestamp directly because of some nanoseconds imprecision with the floats
798+
assert_eq!(
799+
key.expires_at.unwrap().unix_timestamp(),
800+
expires_at.unix_timestamp()
801+
);
804802
assert_eq!(key.indexes, vec!["*".to_string()]);
805803

806804
let keys = client.get_keys().await.unwrap();
@@ -809,9 +807,10 @@ mod tests {
809807

810808
assert_eq!(remote_key.actions, vec![Action::DocumentsAdd]);
811809
assert_eq!(remote_key.description, description);
810+
// We can't compare the two timestamp directly because of some nanoseconds imprecision with the floats
812811
assert_eq!(
813-
remote_key.expires_at,
814-
Some("3022-02-09T01:32:46Z".to_string())
812+
remote_key.expires_at.unwrap().unix_timestamp(),
813+
expires_at.unix_timestamp()
815814
);
816815
assert_eq!(remote_key.indexes, vec!["*".to_string()]);
817816

@@ -821,7 +820,7 @@ mod tests {
821820
#[meilisearch_test]
822821
async fn test_error_update_key(mut client: Client, description: String) {
823822
let key = KeyBuilder::new(description.clone());
824-
let mut key = client.create_key(key).await.unwrap();
823+
let key = client.create_key(key).await.unwrap();
825824

826825
// ==> Invalid index name
827826
/* TODO: uncomment once meilisearch fix this bug: https://github.com/meilisearch/meilisearch/issues/2158
@@ -838,20 +837,6 @@ mod tests {
838837
));
839838
*/
840839

841-
// ==> Invalid expires_at
842-
key.expires_at = Some("That’s totally not a date".to_string());
843-
let error = client.update_key(&key).await.unwrap_err();
844-
845-
assert!(matches!(
846-
error,
847-
Error::Meilisearch(MeilisearchError {
848-
error_code: ErrorCode::InvalidApiKeyExpiresAt,
849-
error_type: ErrorType::InvalidRequest,
850-
..
851-
})
852-
));
853-
key.expires_at = None;
854-
855840
// ==> executing the action without enough right
856841
let no_right_key = KeyBuilder::new(&description);
857842
let no_right_key = client.create_key(no_right_key).await.unwrap();

src/indexes.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ use crate::{client::Client, document::*, errors::Error, request::*, search::*, t
22
use serde::{de::DeserializeOwned, Deserialize, Serialize};
33
use serde_json::json;
44
use std::{collections::HashMap, fmt::Display, time::Duration};
5+
use time::OffsetDateTime;
56

67
#[derive(Deserialize, Debug)]
78
#[allow(non_snake_case)]
89
pub struct JsonIndex {
910
pub uid: String,
1011
pub primaryKey: Option<String>,
11-
pub createdAt: String, // TODO: use a chrono date
12-
pub updatedAt: String, // TODO: use a chrono date
12+
#[serde(with = "time::serde::rfc3339")]
13+
pub createdAt: OffsetDateTime,
14+
#[serde(with = "time::serde::rfc3339")]
15+
pub updatedAt: OffsetDateTime,
1316
}
1417

1518
impl JsonIndex {

src/key.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use serde::{Deserialize, Serialize};
2+
use time::OffsetDateTime;
23

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

@@ -10,17 +11,17 @@ use crate::{client::Client, errors::Error};
1011
pub struct Key {
1112
#[serde(skip_serializing_if = "Vec::is_empty")]
1213
pub actions: Vec<Action>,
13-
#[serde(skip_serializing)]
14-
pub created_at: String, // TODO: use a chrono date
14+
#[serde(skip_serializing, with = "time::serde::rfc3339")]
15+
pub created_at: OffsetDateTime,
1516
pub description: String,
16-
#[serde(skip_serializing_if = "Option::is_none")]
17-
pub expires_at: Option<String>, // TODO: use a chrono date
17+
#[serde(with = "time::serde::rfc3339::option")]
18+
pub expires_at: Option<OffsetDateTime>,
1819
#[serde(skip_serializing_if = "Vec::is_empty")]
1920
pub indexes: Vec<String>,
2021
#[serde(skip_serializing)]
2122
pub key: String,
22-
#[serde(skip_serializing)]
23-
pub updated_at: String, // TODO: use a chrono date
23+
#[serde(skip_serializing, with = "time::serde::rfc3339")]
24+
pub updated_at: OffsetDateTime,
2425
}
2526

2627
impl AsRef<str> for Key {
@@ -59,7 +60,8 @@ impl AsRef<Key> for Key {
5960
pub struct KeyBuilder {
6061
pub actions: Vec<Action>,
6162
pub description: String,
62-
pub expires_at: Option<String>, // TODO: use a chrono date
63+
#[serde(with = "time::serde::rfc3339::option")]
64+
pub expires_at: Option<OffsetDateTime>,
6365
pub indexes: Vec<String>,
6466
}
6567

@@ -115,11 +117,13 @@ impl KeyBuilder {
115117
///
116118
/// ```
117119
/// # use meilisearch_sdk::{key::KeyBuilder};
120+
/// use time::{OffsetDateTime, Duration};
118121
/// let mut builder = KeyBuilder::new("My little lovely test key");
119-
/// builder.with_expires_at("3022-02-09T10:35:58Z".to_string());
122+
/// // create a key that expires in two weeks from now
123+
/// builder.with_expires_at(OffsetDateTime::now_utc() + Duration::WEEK * 2);
120124
/// ```
121-
pub fn with_expires_at(&mut self, expires_at: impl AsRef<str>) -> &mut Self {
122-
self.expires_at = Some(expires_at.as_ref().to_string());
125+
pub fn with_expires_at(&mut self, expires_at: OffsetDateTime) -> &mut Self {
126+
self.expires_at = Some(expires_at);
123127
self
124128
}
125129

src/tasks.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
use serde::Deserialize;
1+
use serde::{Deserialize, Deserializer};
22
use std::time::Duration;
3+
use time::OffsetDateTime;
34

45
use crate::{
56
client::Client, errors::Error, errors::MeilisearchError, indexes::Index, settings::Settings,
@@ -57,13 +58,26 @@ impl AsRef<u64> for FailedTask {
5758
}
5859
}
5960

61+
fn deserialize_duration<'de, D>(deserializer: D) -> Result<std::time::Duration, D::Error>
62+
where
63+
D: Deserializer<'de>,
64+
{
65+
let s = String::deserialize(deserializer)?;
66+
let iso_duration = iso8601_duration::Duration::parse(&s).map_err(serde::de::Error::custom)?;
67+
Ok(iso_duration.to_std())
68+
}
69+
6070
#[derive(Deserialize, Debug, Clone)]
6171
#[serde(rename_all = "camelCase")]
6272
pub struct ProcessedTask {
63-
pub duration: String, // TODO deserialize to Duration
64-
pub enqueued_at: String, // TODO deserialize to datetime
65-
pub started_at: String, // TODO deserialize to datetime
66-
pub finished_at: String, // TODO deserialize to datetime
73+
#[serde(deserialize_with = "deserialize_duration")]
74+
pub duration: Duration,
75+
#[serde(with = "time::serde::rfc3339")]
76+
pub enqueued_at: OffsetDateTime,
77+
#[serde(with = "time::serde::rfc3339")]
78+
pub started_at: OffsetDateTime,
79+
#[serde(with = "time::serde::rfc3339")]
80+
pub finished_at: OffsetDateTime,
6781
pub index_uid: String,
6882
#[serde(flatten)]
6983
pub update_type: TaskType,
@@ -79,7 +93,8 @@ impl AsRef<u64> for ProcessedTask {
7993
#[derive(Debug, Clone, Deserialize)]
8094
#[serde(rename_all = "camelCase")]
8195
pub struct EnqueuedTask {
82-
pub enqueued_at: String, // TODO deserialize to datetime
96+
#[serde(with = "time::serde::rfc3339")]
97+
pub enqueued_at: OffsetDateTime,
8398
pub index_uid: String,
8499
#[serde(flatten)]
85100
pub update_type: TaskType,
@@ -392,7 +407,13 @@ mod test {
392407
}
393408

394409
#[test]
395-
fn test_deserialize_enqueued_task() {
410+
fn test_deserialize_task() {
411+
let datetime = OffsetDateTime::parse(
412+
"2022-02-03T13:02:38.369634Z",
413+
&::time::format_description::well_known::Rfc3339,
414+
)
415+
.unwrap();
416+
396417
let task: Task = serde_json::from_str(
397418
r#"
398419
{
@@ -415,7 +436,7 @@ mod test {
415436
uid: 12,
416437
}
417438
}
418-
if enqueued_at == "2022-02-03T13:02:38.369634Z" && index_uid == "mieli"));
439+
if enqueued_at == datetime && index_uid == "mieli"));
419440

420441
let task: Task = serde_json::from_str(
421442
r#"
@@ -482,9 +503,11 @@ mod test {
482503
})
483504
},
484505
uid: 14,
506+
duration,
485507
..
486508
}
487509
}
510+
if duration == Duration::from_secs_f32(10.848957061)
488511
));
489512
}
490513

0 commit comments

Comments
 (0)