Move tasks route to deserr

This commit is contained in:
Loïc Lecrenier 2023-01-11 20:33:07 +01:00
parent 387874ea26
commit a09b6a341d
7 changed files with 372 additions and 604 deletions

View File

@ -9,7 +9,7 @@ actix-web = { version = "4.2.1", default-features = false }
anyhow = "1.0.65" anyhow = "1.0.65"
convert_case = "0.6.0" convert_case = "0.6.0"
csv = "1.1.6" csv = "1.1.6"
deserr = { version = "0.1.4", features = ["serde-json"] } deserr = "0.1.4"
either = { version = "1.6.1", features = ["serde"] } either = { version = "1.6.1", features = ["serde"] }
enum-iterator = "1.1.3" enum-iterator = "1.1.3"
file-store = { path = "../file-store" } file-store = { path = "../file-store" }

View File

@ -19,7 +19,7 @@ byte-unit = { version = "4.0.14", default-features = false, features = ["std", "
bytes = "1.2.1" bytes = "1.2.1"
clap = { version = "4.0.9", features = ["derive", "env"] } clap = { version = "4.0.9", features = ["derive", "env"] }
crossbeam-channel = "0.5.6" crossbeam-channel = "0.5.6"
deserr = { version = "0.1.4", features = ["serde-json"] } deserr = "0.1.4"
dump = { path = "../dump" } dump = { path = "../dump" }
either = "1.8.0" either = "1.8.0"
env_logger = "0.9.1" env_logger = "0.9.1"

View File

@ -7,8 +7,8 @@ use serde_json::Value;
use super::{find_user_id, Analytics, DocumentDeletionKind}; use super::{find_user_id, Analytics, DocumentDeletionKind};
use crate::routes::indexes::documents::UpdateDocumentsQuery; use crate::routes::indexes::documents::UpdateDocumentsQuery;
use crate::routes::tasks::TasksFilterQueryRaw;
use crate::Opt; use crate::Opt;
use crate::routes::tasks::TasksFilterQuery;
pub struct MockAnalytics { pub struct MockAnalytics {
instance_uid: Option<InstanceUid>, instance_uid: Option<InstanceUid>,
@ -58,6 +58,6 @@ impl Analytics for MockAnalytics {
_request: &HttpRequest, _request: &HttpRequest,
) { ) {
} }
fn get_tasks(&self, _query: &TasksFilterQueryRaw, _request: &HttpRequest) {} fn get_tasks(&self, _query: &TasksFilterQuery, _request: &HttpRequest) {}
fn health_seen(&self, _request: &HttpRequest) {} fn health_seen(&self, _request: &HttpRequest) {}
} }

View File

@ -15,7 +15,7 @@ use platform_dirs::AppDirs;
use serde_json::Value; use serde_json::Value;
use crate::routes::indexes::documents::UpdateDocumentsQuery; use crate::routes::indexes::documents::UpdateDocumentsQuery;
use crate::routes::tasks::TasksFilterQueryRaw; use crate::routes::tasks::TasksFilterQuery;
// if we are in debug mode OR the analytics feature is disabled // if we are in debug mode OR the analytics feature is disabled
// the `SegmentAnalytics` point to the mock instead of the real analytics // the `SegmentAnalytics` point to the mock instead of the real analytics
@ -94,7 +94,7 @@ pub trait Analytics: Sync + Send {
); );
// this method should be called to aggregate the get tasks requests. // this method should be called to aggregate the get tasks requests.
fn get_tasks(&self, query: &TasksFilterQueryRaw, request: &HttpRequest); fn get_tasks(&self, query: &TasksFilterQuery, request: &HttpRequest);
// this method should be called to aggregate a add documents request // this method should be called to aggregate a add documents request
fn health_seen(&self, request: &HttpRequest); fn health_seen(&self, request: &HttpRequest);

View File

@ -1,10 +1,12 @@
use std::num::ParseIntError;
use std::str::FromStr; use std::str::FromStr;
use actix_web::web::Data; use actix_web::web::Data;
use actix_web::{web, HttpRequest, HttpResponse}; use actix_web::{web, HttpRequest, HttpResponse};
use index_scheduler::error::DateField; use deserr::DeserializeFromValue;
use index_scheduler::{IndexScheduler, Query, TaskId}; use index_scheduler::{IndexScheduler, Query, TaskId};
use meilisearch_types::error::ResponseError; use meilisearch_types::error::{deserr_codes::*, TakeErrorMessage};
use meilisearch_types::error::{DeserrError, ResponseError};
use meilisearch_types::index_uid::IndexUid; use meilisearch_types::index_uid::IndexUid;
use meilisearch_types::settings::{Settings, Unchecked}; use meilisearch_types::settings::{Settings, Unchecked};
use meilisearch_types::star_or::StarOr; use meilisearch_types::star_or::StarOr;
@ -14,14 +16,16 @@ use meilisearch_types::tasks::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_cs::vec::CS; use serde_cs::vec::CS;
use serde_json::json; use serde_json::json;
use time::{Duration, OffsetDateTime}; use time::format_description::well_known::Rfc3339;
use time::macros::format_description;
use time::{Date, Duration, OffsetDateTime, Time};
use tokio::task; use tokio::task;
use self::date_deserializer::{deserialize_date, DeserializeDateOption};
use super::{fold_star_or, SummarizedTaskView}; use super::{fold_star_or, SummarizedTaskView};
use crate::analytics::Analytics; use crate::analytics::Analytics;
use crate::extractors::authentication::policies::*; use crate::extractors::authentication::policies::*;
use crate::extractors::authentication::GuardedData; use crate::extractors::authentication::GuardedData;
use crate::extractors::query_parameters::QueryParameter;
use crate::extractors::sequential_extractor::SeqHandler; use crate::extractors::sequential_extractor::SeqHandler;
const DEFAULT_LIMIT: fn() -> u32 = || 20; const DEFAULT_LIMIT: fn() -> u32 = || 20;
@ -160,308 +164,124 @@ impl From<Details> for DetailsView {
} }
} }
#[derive(Deserialize, Debug)] fn parse_option_cs<T: FromStr>(
#[serde(rename_all = "camelCase", deny_unknown_fields)] s: Option<CS<String>>,
pub struct TaskCommonQueryRaw { ) -> Result<Option<Vec<T>>, TakeErrorMessage<T::Err>> {
pub uids: Option<CS<String>>, if let Some(s) = s {
pub canceled_by: Option<CS<String>>, s.into_iter()
pub types: Option<CS<StarOr<String>>>, .map(|s| T::from_str(&s))
pub statuses: Option<CS<StarOr<String>>>, .collect::<Result<Vec<T>, T::Err>>()
pub index_uids: Option<CS<StarOr<String>>>, .map_err(TakeErrorMessage)
} .map(Some)
impl TaskCommonQueryRaw {
fn validate(self) -> Result<TaskCommonQuery, ResponseError> {
let Self { uids, canceled_by, types, statuses, index_uids } = self;
let uids = if let Some(uids) = uids {
Some(
uids.into_iter()
.map(|uid_string| {
uid_string.parse::<u32>().map_err(|_e| {
index_scheduler::Error::InvalidTaskUids { task_uid: uid_string }.into()
})
})
.collect::<Result<Vec<u32>, ResponseError>>()?,
)
} else { } else {
None Ok(None)
};
let canceled_by = if let Some(canceled_by) = canceled_by {
Some(
canceled_by
.into_iter()
.map(|canceled_by_string| {
canceled_by_string.parse::<u32>().map_err(|_e| {
index_scheduler::Error::InvalidTaskCanceledBy {
canceled_by: canceled_by_string,
} }
.into() }
}) fn parse_option_cs_star_or<T: FromStr>(
}) s: Option<CS<StarOr<String>>>,
.collect::<Result<Vec<u32>, ResponseError>>()?, ) -> Result<Option<Vec<T>>, TakeErrorMessage<T::Err>> {
) if let Some(s) = s.and_then(fold_star_or) as Option<Vec<String>> {
s.into_iter()
.map(|s| T::from_str(&s))
.collect::<Result<Vec<T>, T::Err>>()
.map_err(TakeErrorMessage)
.map(Some)
} else { } else {
None Ok(None)
}; }
}
let types = if let Some(types) = types.and_then(fold_star_or) as Option<Vec<String>> { fn parse_option_str<T: FromStr>(s: Option<String>) -> Result<Option<T>, TakeErrorMessage<T::Err>> {
Some( if let Some(s) = s {
types T::from_str(&s).map_err(TakeErrorMessage).map(Some)
.into_iter()
.map(|type_string| {
Kind::from_str(&type_string).map_err(|_e| {
index_scheduler::Error::InvalidTaskTypes { type_: type_string }.into()
})
})
.collect::<Result<Vec<Kind>, ResponseError>>()?,
)
} else { } else {
None Ok(None)
};
let statuses = if let Some(statuses) =
statuses.and_then(fold_star_or) as Option<Vec<String>>
{
Some(
statuses
.into_iter()
.map(|status_string| {
Status::from_str(&status_string).map_err(|_e| {
index_scheduler::Error::InvalidTaskStatuses { status: status_string }
.into()
})
})
.collect::<Result<Vec<Status>, ResponseError>>()?,
)
} else {
None
};
let index_uids =
if let Some(index_uids) = index_uids.and_then(fold_star_or) as Option<Vec<String>> {
Some(
index_uids
.into_iter()
.map(|index_uid_string| {
IndexUid::from_str(&index_uid_string)
.map(|index_uid| index_uid.to_string())
.map_err(|_e| {
index_scheduler::Error::InvalidIndexUid {
index_uid: index_uid_string,
}
.into()
})
})
.collect::<Result<Vec<String>, ResponseError>>()?,
)
} else {
None
};
Ok(TaskCommonQuery { types, uids, canceled_by, statuses, index_uids })
} }
} }
#[derive(Deserialize, Debug)] fn parse_str<T: FromStr>(s: String) -> Result<T, TakeErrorMessage<T::Err>> {
#[serde(rename_all = "camelCase", deny_unknown_fields)] T::from_str(&s).map_err(TakeErrorMessage)
pub struct TaskDateQueryRaw {
pub after_enqueued_at: Option<String>,
pub before_enqueued_at: Option<String>,
pub after_started_at: Option<String>,
pub before_started_at: Option<String>,
pub after_finished_at: Option<String>,
pub before_finished_at: Option<String>,
} }
impl TaskDateQueryRaw { #[derive(Debug, DeserializeFromValue)]
fn validate(self) -> Result<TaskDateQuery, ResponseError> { #[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
let Self {
after_enqueued_at,
before_enqueued_at,
after_started_at,
before_started_at,
after_finished_at,
before_finished_at,
} = self;
let mut query = TaskDateQuery {
after_enqueued_at: None,
before_enqueued_at: None,
after_started_at: None,
before_started_at: None,
after_finished_at: None,
before_finished_at: None,
};
for (field_name, string_value, before_or_after, dest) in [
(
DateField::AfterEnqueuedAt,
after_enqueued_at,
DeserializeDateOption::After,
&mut query.after_enqueued_at,
),
(
DateField::BeforeEnqueuedAt,
before_enqueued_at,
DeserializeDateOption::Before,
&mut query.before_enqueued_at,
),
(
DateField::AfterStartedAt,
after_started_at,
DeserializeDateOption::After,
&mut query.after_started_at,
),
(
DateField::BeforeStartedAt,
before_started_at,
DeserializeDateOption::Before,
&mut query.before_started_at,
),
(
DateField::AfterFinishedAt,
after_finished_at,
DeserializeDateOption::After,
&mut query.after_finished_at,
),
(
DateField::BeforeFinishedAt,
before_finished_at,
DeserializeDateOption::Before,
&mut query.before_finished_at,
),
] {
if let Some(string_value) = string_value {
*dest = Some(deserialize_date(field_name, &string_value, before_or_after)?);
}
}
Ok(query)
}
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct TasksFilterQueryRaw {
#[serde(flatten)]
pub common: TaskCommonQueryRaw,
#[serde(default = "DEFAULT_LIMIT")]
pub limit: u32,
pub from: Option<TaskId>,
#[serde(flatten)]
pub dates: TaskDateQueryRaw,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct TaskDeletionOrCancelationQueryRaw {
#[serde(flatten)]
pub common: TaskCommonQueryRaw,
#[serde(flatten)]
pub dates: TaskDateQueryRaw,
}
impl TasksFilterQueryRaw {
fn validate(self) -> Result<TasksFilterQuery, ResponseError> {
let Self { common, limit, from, dates } = self;
let common = common.validate()?;
let dates = dates.validate()?;
Ok(TasksFilterQuery { common, limit, from, dates })
}
}
impl TaskDeletionOrCancelationQueryRaw {
fn validate(self) -> Result<TaskDeletionOrCancelationQuery, ResponseError> {
let Self { common, dates } = self;
let common = common.validate()?;
let dates = dates.validate()?;
Ok(TaskDeletionOrCancelationQuery { common, dates })
}
}
#[derive(Serialize, Debug)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct TaskDateQuery {
#[serde(
default,
skip_serializing_if = "Option::is_none",
serialize_with = "time::serde::rfc3339::option::serialize"
)]
after_enqueued_at: Option<OffsetDateTime>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
serialize_with = "time::serde::rfc3339::option::serialize"
)]
before_enqueued_at: Option<OffsetDateTime>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
serialize_with = "time::serde::rfc3339::option::serialize"
)]
after_started_at: Option<OffsetDateTime>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
serialize_with = "time::serde::rfc3339::option::serialize"
)]
before_started_at: Option<OffsetDateTime>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
serialize_with = "time::serde::rfc3339::option::serialize"
)]
after_finished_at: Option<OffsetDateTime>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
serialize_with = "time::serde::rfc3339::option::serialize"
)]
before_finished_at: Option<OffsetDateTime>,
}
#[derive(Debug)]
pub struct TaskCommonQuery {
types: Option<Vec<Kind>>,
uids: Option<Vec<TaskId>>,
canceled_by: Option<Vec<TaskId>>,
statuses: Option<Vec<Status>>,
index_uids: Option<Vec<String>>,
}
#[derive(Debug)]
pub struct TasksFilterQuery { pub struct TasksFilterQuery {
limit: u32, #[deserr(error = DeserrError<InvalidTaskLimit>, default = DEFAULT_LIMIT(), from(String) = parse_str::<u32> -> TakeErrorMessage<ParseIntError>)]
from: Option<TaskId>, pub limit: u32,
common: TaskCommonQuery, #[deserr(error = DeserrError<InvalidTaskFrom>, from(Option<String>) = parse_option_str::<TaskId> -> TakeErrorMessage<ParseIntError>)]
dates: TaskDateQuery, pub from: Option<TaskId>,
#[deserr(error = DeserrError<InvalidTaskUids>, from(Option<CS<String>>) = parse_option_cs::<u32> -> TakeErrorMessage<ParseIntError>)]
pub uids: Option<Vec<u32>>,
#[deserr(error = DeserrError<InvalidTaskCanceledBy>, from(Option<CS<String>>) = parse_option_cs::<u32> -> TakeErrorMessage<ParseIntError>)]
pub canceled_by: Option<Vec<u32>>,
#[deserr(error = DeserrError<InvalidTaskTypes>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Kind> -> TakeErrorMessage<ResponseError>)]
pub types: Option<Vec<Kind>>,
#[deserr(error = DeserrError<InvalidTaskStatuses>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Status> -> TakeErrorMessage<ResponseError>)]
pub statuses: Option<Vec<Status>>,
#[deserr(error = DeserrError<InvalidIndexUid>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<IndexUid> -> TakeErrorMessage<ResponseError>)]
pub index_uids: Option<Vec<IndexUid>>,
#[deserr(error = DeserrError<InvalidTaskAfterEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
pub after_enqueued_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskBeforeEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
pub before_enqueued_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskAfterStartedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
pub after_started_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskBeforeStartedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
pub before_started_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskAfterFinishedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
pub after_finished_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskBeforeFinishedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
pub before_finished_at: Option<OffsetDateTime>,
} }
#[derive(Debug)] #[derive(Deserialize, Debug, DeserializeFromValue)]
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
pub struct TaskDeletionOrCancelationQuery { pub struct TaskDeletionOrCancelationQuery {
common: TaskCommonQuery, #[deserr(error = DeserrError<InvalidTaskUids>, from(Option<CS<String>>) = parse_option_cs::<u32> -> TakeErrorMessage<ParseIntError>)]
dates: TaskDateQuery, pub uids: Option<Vec<u32>>,
#[deserr(error = DeserrError<InvalidTaskCanceledBy>, from(Option<CS<String>>) = parse_option_cs::<u32> -> TakeErrorMessage<ParseIntError>)]
pub canceled_by: Option<Vec<u32>>,
#[deserr(error = DeserrError<InvalidTaskTypes>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Kind> -> TakeErrorMessage<ResponseError>)]
pub types: Option<Vec<Kind>>,
#[deserr(error = DeserrError<InvalidTaskStatuses>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Status> -> TakeErrorMessage<ResponseError>)]
pub statuses: Option<Vec<Status>>,
#[deserr(error = DeserrError<InvalidIndexUid>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<IndexUid> -> TakeErrorMessage<ResponseError>)]
pub index_uids: Option<Vec<IndexUid>>,
#[deserr(error = DeserrError<InvalidTaskAfterEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
pub after_enqueued_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskBeforeEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
pub before_enqueued_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskAfterStartedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
pub after_started_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskBeforeStartedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
pub before_started_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskAfterFinishedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
pub after_finished_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskBeforeFinishedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
pub before_finished_at: Option<OffsetDateTime>,
} }
async fn cancel_tasks( async fn cancel_tasks(
index_scheduler: GuardedData<ActionPolicy<{ actions::TASKS_CANCEL }>, Data<IndexScheduler>>, index_scheduler: GuardedData<ActionPolicy<{ actions::TASKS_CANCEL }>, Data<IndexScheduler>>,
params: web::Query<TaskDeletionOrCancelationQueryRaw>, params: QueryParameter<TaskDeletionOrCancelationQuery, DeserrError>,
req: HttpRequest, req: HttpRequest,
analytics: web::Data<dyn Analytics>, analytics: web::Data<dyn Analytics>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {
let query = params.into_inner().validate()?;
let TaskDeletionOrCancelationQuery { let TaskDeletionOrCancelationQuery {
common: TaskCommonQuery { types, uids, canceled_by, statuses, index_uids }, types,
dates: uids,
TaskDateQuery { canceled_by,
statuses,
index_uids,
after_enqueued_at, after_enqueued_at,
before_enqueued_at, before_enqueued_at,
after_started_at, after_started_at,
before_started_at, before_started_at,
after_finished_at, after_finished_at,
before_finished_at, before_finished_at,
}, } = params.into_inner();
} = query;
analytics.publish( analytics.publish(
"Tasks Canceled".to_string(), "Tasks Canceled".to_string(),
@ -486,7 +306,7 @@ async fn cancel_tasks(
from: None, from: None,
statuses, statuses,
types, types,
index_uids, index_uids: index_uids.map(|xs| xs.into_iter().map(|s| s.to_string()).collect()),
uids, uids,
canceled_by, canceled_by,
before_enqueued_at, before_enqueued_at,
@ -517,22 +337,24 @@ async fn cancel_tasks(
async fn delete_tasks( async fn delete_tasks(
index_scheduler: GuardedData<ActionPolicy<{ actions::TASKS_DELETE }>, Data<IndexScheduler>>, index_scheduler: GuardedData<ActionPolicy<{ actions::TASKS_DELETE }>, Data<IndexScheduler>>,
params: web::Query<TaskDeletionOrCancelationQueryRaw>, params: QueryParameter<TaskDeletionOrCancelationQuery, DeserrError>,
req: HttpRequest, req: HttpRequest,
analytics: web::Data<dyn Analytics>, analytics: web::Data<dyn Analytics>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {
let TaskDeletionOrCancelationQuery { let TaskDeletionOrCancelationQuery {
common: TaskCommonQuery { types, uids, canceled_by, statuses, index_uids }, types,
dates: uids,
TaskDateQuery { canceled_by,
statuses,
index_uids,
after_enqueued_at, after_enqueued_at,
before_enqueued_at, before_enqueued_at,
after_started_at, after_started_at,
before_started_at, before_started_at,
after_finished_at, after_finished_at,
before_finished_at, before_finished_at,
}, } = params.into_inner();
} = params.into_inner().validate()?;
analytics.publish( analytics.publish(
"Tasks Deleted".to_string(), "Tasks Deleted".to_string(),
@ -557,7 +379,7 @@ async fn delete_tasks(
from: None, from: None,
statuses, statuses,
types, types,
index_uids, index_uids: index_uids.map(|xs| xs.into_iter().map(|s| s.to_string()).collect()),
uids, uids,
canceled_by, canceled_by,
after_enqueued_at, after_enqueued_at,
@ -596,26 +418,28 @@ pub struct AllTasks {
async fn get_tasks( async fn get_tasks(
index_scheduler: GuardedData<ActionPolicy<{ actions::TASKS_GET }>, Data<IndexScheduler>>, index_scheduler: GuardedData<ActionPolicy<{ actions::TASKS_GET }>, Data<IndexScheduler>>,
params: web::Query<TasksFilterQueryRaw>, params: QueryParameter<TasksFilterQuery, DeserrError>,
req: HttpRequest, req: HttpRequest,
analytics: web::Data<dyn Analytics>, analytics: web::Data<dyn Analytics>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {
let params = params.into_inner();
analytics.get_tasks(&params, &req); analytics.get_tasks(&params, &req);
let TasksFilterQuery { let TasksFilterQuery {
common: TaskCommonQuery { types, uids, canceled_by, statuses, index_uids }, types,
uids,
canceled_by,
statuses,
index_uids,
limit, limit,
from, from,
dates:
TaskDateQuery {
after_enqueued_at, after_enqueued_at,
before_enqueued_at, before_enqueued_at,
after_started_at, after_started_at,
before_started_at, before_started_at,
after_finished_at, after_finished_at,
before_finished_at, before_finished_at,
}, } = params;
} = params.into_inner().validate()?;
// We +1 just to know if there is more after this "page" or not. // We +1 just to know if there is more after this "page" or not.
let limit = limit.saturating_add(1); let limit = limit.saturating_add(1);
@ -625,7 +449,7 @@ async fn get_tasks(
from, from,
statuses, statuses,
types, types,
index_uids, index_uids: index_uids.map(|xs| xs.into_iter().map(|s| s.to_string()).collect()),
uids, uids,
canceled_by, canceled_by,
before_enqueued_at, before_enqueued_at,
@ -692,23 +516,15 @@ async fn get_task(
} }
} }
pub(crate) mod date_deserializer {
use index_scheduler::error::DateField;
use meilisearch_types::error::ResponseError;
use time::format_description::well_known::Rfc3339;
use time::macros::format_description;
use time::{Date, Duration, OffsetDateTime, Time};
pub enum DeserializeDateOption { pub enum DeserializeDateOption {
Before, Before,
After, After,
} }
pub fn deserialize_date( pub fn deserialize_date(
field_name: DateField,
value: &str, value: &str,
option: DeserializeDateOption, option: DeserializeDateOption,
) -> std::result::Result<OffsetDateTime, ResponseError> { ) -> std::result::Result<OffsetDateTime, TakeErrorMessage<InvalidTaskDateError>> {
// We can't parse using time's rfc3339 format, since then we won't know what part of the // We can't parse using time's rfc3339 format, since then we won't know what part of the
// datetime was not explicitly specified, and thus we won't be able to increment it to the // datetime was not explicitly specified, and thus we won't be able to increment it to the
// next step. // next step.
@ -730,295 +546,247 @@ pub(crate) mod date_deserializer {
} }
} }
} else { } else {
Err(index_scheduler::Error::InvalidTaskDate { Err(TakeErrorMessage(InvalidTaskDateError(value.to_owned())))
field: field_name,
date: value.to_string(),
}
.into())
} }
} }
pub fn deserialize_date_before(
value: Option<String>,
) -> std::result::Result<Option<OffsetDateTime>, TakeErrorMessage<InvalidTaskDateError>> {
if let Some(value) = value {
let date = deserialize_date(&value, DeserializeDateOption::Before)?;
Ok(Some(date))
} else {
Ok(None)
} }
}
pub fn deserialize_date_after(
value: Option<String>,
) -> std::result::Result<Option<OffsetDateTime>, TakeErrorMessage<InvalidTaskDateError>> {
if let Some(value) = value {
let date = deserialize_date(&value, DeserializeDateOption::After)?;
Ok(Some(date))
} else {
Ok(None)
}
}
#[derive(Debug)]
pub struct InvalidTaskDateError(String);
impl std::fmt::Display for InvalidTaskDateError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "`{}` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", self.0)
}
}
impl std::error::Error for InvalidTaskDateError {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use deserr::DeserializeFromValue;
use meili_snap::snapshot; use meili_snap::snapshot;
use meilisearch_types::error::DeserrError;
use crate::routes::tasks::{TaskDeletionOrCancelationQueryRaw, TasksFilterQueryRaw}; use crate::{
extractors::query_parameters::QueryParameter,
routes::tasks::{TaskDeletionOrCancelationQuery, TasksFilterQuery},
};
fn deserr_query_params<T>(j: &str) -> Result<T, actix_web::Error>
where
T: DeserializeFromValue<DeserrError>,
{
QueryParameter::<T, DeserrError>::from_query(j).map(|p| p.0)
}
#[test] #[test]
fn deserialize_task_filter_dates() { fn deserialize_task_filter_dates() {
{ {
let json = r#" { let params = "afterEnqueuedAt=2021-12-03&beforeEnqueuedAt=2021-12-03&afterStartedAt=2021-12-03&beforeStartedAt=2021-12-03&afterFinishedAt=2021-12-03&beforeFinishedAt=2021-12-03";
"afterEnqueuedAt": "2021-12-03", let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
"beforeEnqueuedAt": "2021-12-03",
"afterStartedAt": "2021-12-03", snapshot!(format!("{:?}", query.after_enqueued_at.unwrap()), @"2021-12-04 0:00:00.0 +00:00:00");
"beforeStartedAt": "2021-12-03", snapshot!(format!("{:?}", query.before_enqueued_at.unwrap()), @"2021-12-03 0:00:00.0 +00:00:00");
"afterFinishedAt": "2021-12-03", snapshot!(format!("{:?}", query.after_started_at.unwrap()), @"2021-12-04 0:00:00.0 +00:00:00");
"beforeFinishedAt": "2021-12-03" snapshot!(format!("{:?}", query.before_started_at.unwrap()), @"2021-12-03 0:00:00.0 +00:00:00");
} "#; snapshot!(format!("{:?}", query.after_finished_at.unwrap()), @"2021-12-04 0:00:00.0 +00:00:00");
let query = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) snapshot!(format!("{:?}", query.before_finished_at.unwrap()), @"2021-12-03 0:00:00.0 +00:00:00");
.unwrap()
.validate()
.unwrap();
snapshot!(format!("{:?}", query.dates.after_enqueued_at.unwrap()), @"2021-12-04 0:00:00.0 +00:00:00");
snapshot!(format!("{:?}", query.dates.before_enqueued_at.unwrap()), @"2021-12-03 0:00:00.0 +00:00:00");
snapshot!(format!("{:?}", query.dates.after_started_at.unwrap()), @"2021-12-04 0:00:00.0 +00:00:00");
snapshot!(format!("{:?}", query.dates.before_started_at.unwrap()), @"2021-12-03 0:00:00.0 +00:00:00");
snapshot!(format!("{:?}", query.dates.after_finished_at.unwrap()), @"2021-12-04 0:00:00.0 +00:00:00");
snapshot!(format!("{:?}", query.dates.before_finished_at.unwrap()), @"2021-12-03 0:00:00.0 +00:00:00");
} }
{ {
let json = r#" { "afterEnqueuedAt": "2021-12-03T23:45:23Z", "beforeEnqueuedAt": "2021-12-03T23:45:23Z" } "#; let params =
let query = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) "afterEnqueuedAt=2021-12-03T23:45:23Z&beforeEnqueuedAt=2021-12-03T23:45:23Z";
.unwrap() let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
.validate() snapshot!(format!("{:?}", query.after_enqueued_at.unwrap()), @"2021-12-03 23:45:23.0 +00:00:00");
.unwrap(); snapshot!(format!("{:?}", query.before_enqueued_at.unwrap()), @"2021-12-03 23:45:23.0 +00:00:00");
snapshot!(format!("{:?}", query.dates.after_enqueued_at.unwrap()), @"2021-12-03 23:45:23.0 +00:00:00");
snapshot!(format!("{:?}", query.dates.before_enqueued_at.unwrap()), @"2021-12-03 23:45:23.0 +00:00:00");
} }
{ {
let json = r#" { "afterEnqueuedAt": "1997-11-12T09:55:06-06:20" } "#; let params = "afterEnqueuedAt=1997-11-12T09:55:06-06:20";
let query = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
.unwrap() snapshot!(format!("{:?}", query.after_enqueued_at.unwrap()), @"1997-11-12 9:55:06.0 -06:20:00");
.validate()
.unwrap();
snapshot!(format!("{:?}", query.dates.after_enqueued_at.unwrap()), @"1997-11-12 9:55:06.0 -06:20:00");
} }
{ {
let json = r#" { "afterEnqueuedAt": "1997-11-12T09:55:06+00:00" } "#; let params = "afterEnqueuedAt=1997-11-12T09:55:06%2B00:00";
let query = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
.unwrap() snapshot!(format!("{:?}", query.after_enqueued_at.unwrap()), @"1997-11-12 9:55:06.0 +00:00:00");
.validate()
.unwrap();
snapshot!(format!("{:?}", query.dates.after_enqueued_at.unwrap()), @"1997-11-12 9:55:06.0 +00:00:00");
} }
{ {
let json = r#" { "afterEnqueuedAt": "1997-11-12T09:55:06.200000300Z" } "#; let params = "afterEnqueuedAt=1997-11-12T09:55:06.200000300Z";
let query = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
.unwrap() snapshot!(format!("{:?}", query.after_enqueued_at.unwrap()), @"1997-11-12 9:55:06.2000003 +00:00:00");
.validate()
.unwrap();
snapshot!(format!("{:?}", query.dates.after_enqueued_at.unwrap()), @"1997-11-12 9:55:06.2000003 +00:00:00");
} }
{ {
let json = r#" { "afterFinishedAt": "2021" } "#; let params = "afterFinishedAt=2021";
let err = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
.unwrap() snapshot!(format!("{err}"), @"`2021` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterFinishedAt`.");
.validate()
.unwrap_err();
snapshot!(format!("{err}"), @"Task `afterFinishedAt` `2021` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.");
} }
{ {
let json = r#" { "beforeFinishedAt": "2021" } "#; let params = "beforeFinishedAt=2021";
let err = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
.unwrap() snapshot!(format!("{err}"), @"`2021` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeFinishedAt`.");
.validate()
.unwrap_err();
snapshot!(format!("{err}"), @"Task `beforeFinishedAt` `2021` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.");
} }
{ {
let json = r#" { "afterEnqueuedAt": "2021-12" } "#; let params = "afterEnqueuedAt=2021-12";
let err = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
.unwrap() snapshot!(format!("{err}"), @"`2021-12` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterEnqueuedAt`.");
.validate()
.unwrap_err();
snapshot!(format!("{err}"), @"Task `afterEnqueuedAt` `2021-12` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.");
} }
{ {
let json = r#" { "beforeEnqueuedAt": "2021-12-03T23" } "#; let params = "beforeEnqueuedAt=2021-12-03T23";
let err = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
.unwrap() snapshot!(format!("{err}"), @"`2021-12-03T23` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeEnqueuedAt`.");
.validate()
.unwrap_err();
snapshot!(format!("{err}"), @"Task `beforeEnqueuedAt` `2021-12-03T23` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.");
} }
{ {
let json = r#" { "afterStartedAt": "2021-12-03T23:45" } "#; let params = "afterStartedAt=2021-12-03T23:45";
let err = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
.unwrap() snapshot!(format!("{err}"), @"`2021-12-03T23:45` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterStartedAt`.");
.validate() }
.unwrap_err(); {
snapshot!(format!("{err}"), @"Task `afterStartedAt` `2021-12-03T23:45` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format."); let params = "beforeStartedAt=2021-12-03T23:45";
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
let json = r#" { "beforeStartedAt": "2021-12-03T23:45" } "#; snapshot!(format!("{err}"), @"`2021-12-03T23:45` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeStartedAt`.");
let err = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json)
.unwrap()
.validate()
.unwrap_err();
snapshot!(format!("{err}"), @"Task `beforeStartedAt` `2021-12-03T23:45` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.");
} }
} }
#[test] #[test]
fn deserialize_task_filter_uids() { fn deserialize_task_filter_uids() {
{ {
let json = r#" { "uids": "78,1,12,73" } "#; let params = "uids=78,1,12,73";
let query = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
.unwrap() snapshot!(format!("{:?}", query.uids.unwrap()), @"[78, 1, 12, 73]");
.validate()
.unwrap();
snapshot!(format!("{:?}", query.common.uids.unwrap()), @"[78, 1, 12, 73]");
} }
{ {
let json = r#" { "uids": "1" } "#; let params = "uids=1";
let query = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
.unwrap() snapshot!(format!("{:?}", query.uids.unwrap()), @"[1]");
.validate()
.unwrap();
snapshot!(format!("{:?}", query.common.uids.unwrap()), @"[1]");
} }
{ {
let json = r#" { "uids": "78,hello,world" } "#; let params = "uids=78,hello,world";
let err = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
.unwrap() snapshot!(format!("{err}"), @"invalid digit found in string at `.uids`.");
.validate()
.unwrap_err();
snapshot!(format!("{err}"), @"Task uid `hello` is invalid. It should only contain numeric characters.");
} }
{ {
let json = r#" { "uids": "cat" } "#; let params = "uids=cat";
let err = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
.unwrap() snapshot!(format!("{err}"), @"invalid digit found in string at `.uids`.");
.validate()
.unwrap_err();
snapshot!(format!("{err}"), @"Task uid `cat` is invalid. It should only contain numeric characters.");
} }
} }
#[test] #[test]
fn deserialize_task_filter_status() { fn deserialize_task_filter_status() {
{ {
let json = r#" { "statuses": "succeeded,failed,enqueued,processing,canceled" } "#; let params = "statuses=succeeded,failed,enqueued,processing,canceled";
let query = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
.unwrap() snapshot!(format!("{:?}", query.statuses.unwrap()), @"[Succeeded, Failed, Enqueued, Processing, Canceled]");
.validate()
.unwrap();
snapshot!(format!("{:?}", query.common.statuses.unwrap()), @"[Succeeded, Failed, Enqueued, Processing, Canceled]");
} }
{ {
let json = r#" { "statuses": "enqueued" } "#; let params = "statuses=enqueued";
let query = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
.unwrap() snapshot!(format!("{:?}", query.statuses.unwrap()), @"[Enqueued]");
.validate()
.unwrap();
snapshot!(format!("{:?}", query.common.statuses.unwrap()), @"[Enqueued]");
} }
{ {
let json = r#" { "statuses": "finished" } "#; let params = "statuses=finished";
let err = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
.unwrap() snapshot!(format!("{err}"), @"`finished` is not a status. Available status are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. at `.statuses`.");
.validate()
.unwrap_err();
snapshot!(format!("{err}"), @"Task status `finished` is invalid. Available task statuses are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`.");
} }
} }
#[test] #[test]
fn deserialize_task_filter_types() { fn deserialize_task_filter_types() {
{ {
let json = r#" { "types": "documentAdditionOrUpdate,documentDeletion,settingsUpdate,indexCreation,indexDeletion,indexUpdate,indexSwap,taskCancelation,taskDeletion,dumpCreation,snapshotCreation" }"#; let params = "types=documentAdditionOrUpdate,documentDeletion,settingsUpdate,indexCreation,indexDeletion,indexUpdate,indexSwap,taskCancelation,taskDeletion,dumpCreation,snapshotCreation";
let query = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
.unwrap() snapshot!(format!("{:?}", query.types.unwrap()), @"[DocumentAdditionOrUpdate, DocumentDeletion, SettingsUpdate, IndexCreation, IndexDeletion, IndexUpdate, IndexSwap, TaskCancelation, TaskDeletion, DumpCreation, SnapshotCreation]");
.validate()
.unwrap();
snapshot!(format!("{:?}", query.common.types.unwrap()), @"[DocumentAdditionOrUpdate, DocumentDeletion, SettingsUpdate, IndexCreation, IndexDeletion, IndexUpdate, IndexSwap, TaskCancelation, TaskDeletion, DumpCreation, SnapshotCreation]");
} }
{ {
let json = r#" { "types": "settingsUpdate" } "#; let params = "types=settingsUpdate";
let query = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
.unwrap() snapshot!(format!("{:?}", query.types.unwrap()), @"[SettingsUpdate]");
.validate()
.unwrap();
snapshot!(format!("{:?}", query.common.types.unwrap()), @"[SettingsUpdate]");
} }
{ {
let json = r#" { "types": "createIndex" } "#; let params = "types=createIndex";
let err = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
.unwrap() snapshot!(format!("{err}"), @"`createIndex` is not a type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`. at `.types`.");
.validate()
.unwrap_err();
snapshot!(format!("{err}"), @"Task type `createIndex` is invalid. Available task types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`");
} }
} }
#[test] #[test]
fn deserialize_task_filter_index_uids() { fn deserialize_task_filter_index_uids() {
{ {
let json = r#" { "indexUids": "toto,tata-78" }"#; let params = "indexUids=toto,tata-78";
let query = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
.unwrap() snapshot!(format!("{:?}", query.index_uids.unwrap()), @r###"[IndexUid("toto"), IndexUid("tata-78")]"###);
.validate()
.unwrap();
snapshot!(format!("{:?}", query.common.index_uids.unwrap()), @r###"["toto", "tata-78"]"###);
} }
{ {
let json = r#" { "indexUids": "index_a" } "#; let params = "indexUids=index_a";
let query = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
.unwrap() snapshot!(format!("{:?}", query.index_uids.unwrap()), @r###"[IndexUid("index_a")]"###);
.validate()
.unwrap();
snapshot!(format!("{:?}", query.common.index_uids.unwrap()), @r###"["index_a"]"###);
} }
{ {
let json = r#" { "indexUids": "1,hé" } "#; let params = "indexUids=1,hé";
let err = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
.unwrap() snapshot!(format!("{err}"), @"`hé` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). at `.indexUids`.");
.validate()
.unwrap_err();
snapshot!(format!("{err}"), @"hé is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).");
} }
{ {
let json = r#" { "indexUids": "hé" } "#; let params = "indexUids=hé";
let err = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
.unwrap() snapshot!(format!("{err}"), @"`hé` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). at `.indexUids`.");
.validate()
.unwrap_err();
snapshot!(format!("{err}"), @"hé is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).");
} }
} }
#[test] #[test]
fn deserialize_task_filter_general() { fn deserialize_task_filter_general() {
{ {
let json = r#" { "from": 12, "limit": 15, "indexUids": "toto,tata-78", "statuses": "succeeded,enqueued", "afterEnqueuedAt": "2012-04-23", "uids": "1,2,3" }"#; let params = "from=12&limit=15&indexUids=toto,tata-78&statuses=succeeded,enqueued&afterEnqueuedAt=2012-04-23&uids=1,2,3";
let query = let query = deserr_query_params::<TasksFilterQuery>(params).unwrap();
serde_json::from_str::<TasksFilterQueryRaw>(json).unwrap().validate().unwrap(); snapshot!(format!("{:?}", query), @r###"TasksFilterQuery { limit: 15, from: Some(12), uids: Some([1, 2, 3]), canceled_by: None, types: None, statuses: Some([Succeeded, Enqueued]), index_uids: Some([IndexUid("toto"), IndexUid("tata-78")]), after_enqueued_at: Some(2012-04-24 0:00:00.0 +00:00:00), before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None }"###);
snapshot!(format!("{:?}", query), @r###"TasksFilterQuery { limit: 15, from: Some(12), common: TaskCommonQuery { types: None, uids: Some([1, 2, 3]), canceled_by: None, statuses: Some([Succeeded, Enqueued]), index_uids: Some(["toto", "tata-78"]) }, dates: TaskDateQuery { after_enqueued_at: Some(2012-04-24 0:00:00.0 +00:00:00), before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None } }"###);
} }
{ {
// Stars should translate to `None` in the query // Stars should translate to `None` in the query
// Verify value of the default limit // Verify value of the default limit
let json = r#" { "indexUids": "*", "statuses": "succeeded,*", "afterEnqueuedAt": "2012-04-23", "uids": "1,2,3" }"#; let params = "indexUids=*&statuses=succeeded,*&afterEnqueuedAt=2012-04-23&uids=1,2,3";
let query = let query = deserr_query_params::<TasksFilterQuery>(params).unwrap();
serde_json::from_str::<TasksFilterQueryRaw>(json).unwrap().validate().unwrap(); snapshot!(format!("{:?}", query), @"TasksFilterQuery { limit: 20, from: None, uids: Some([1, 2, 3]), canceled_by: None, types: None, statuses: None, index_uids: None, after_enqueued_at: Some(2012-04-24 0:00:00.0 +00:00:00), before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None }");
snapshot!(format!("{:?}", query), @"TasksFilterQuery { limit: 20, from: None, common: TaskCommonQuery { types: None, uids: Some([1, 2, 3]), canceled_by: None, statuses: None, index_uids: None }, dates: TaskDateQuery { after_enqueued_at: Some(2012-04-24 0:00:00.0 +00:00:00), before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None } }");
} }
{ {
// Stars should also translate to `None` in task deletion/cancelation queries // Stars should also translate to `None` in task deletion/cancelation queries
let json = r#" { "indexUids": "*", "statuses": "succeeded,*", "afterEnqueuedAt": "2012-04-23", "uids": "1,2,3" }"#; let params = "indexUids=*&statuses=succeeded,*&afterEnqueuedAt=2012-04-23&uids=1,2,3";
let query = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json) let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
.unwrap() snapshot!(format!("{:?}", query), @"TaskDeletionOrCancelationQuery { uids: Some([1, 2, 3]), canceled_by: None, types: None, statuses: None, index_uids: None, after_enqueued_at: Some(2012-04-24 0:00:00.0 +00:00:00), before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None }");
.validate()
.unwrap();
snapshot!(format!("{:?}", query), @"TaskDeletionOrCancelationQuery { common: TaskCommonQuery { types: None, uids: Some([1, 2, 3]), canceled_by: None, statuses: None, index_uids: None }, dates: TaskDateQuery { after_enqueued_at: Some(2012-04-24 0:00:00.0 +00:00:00), before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None } }");
} }
{ {
// Stars in uids not allowed // Stars in uids not allowed
let json = r#" { "uids": "*" }"#; let params = "uids=*";
let err = let err = deserr_query_params::<TasksFilterQuery>(params).unwrap_err();
serde_json::from_str::<TasksFilterQueryRaw>(json).unwrap().validate().unwrap_err(); snapshot!(format!("{err}"), @"invalid digit found in string at `.uids`.");
snapshot!(format!("{err}"), @"Task uid `*` is invalid. It should only contain numeric characters.");
} }
{ {
// From not allowed in task deletion/cancelation queries // From not allowed in task deletion/cancelation queries
let json = r#" { "from": 12 }"#; let params = "from=12";
let err = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json).unwrap_err(); let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
snapshot!(format!("{err}"), @"unknown field `from` at line 1 column 15"); snapshot!(format!("{err}"), @"Json deserialize error: unknown field `from`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.");
} }
{ {
// Limit not allowed in task deletion/cancelation queries // Limit not allowed in task deletion/cancelation queries
let json = r#" { "limit": 12 }"#; let params = "limit=12";
let err = serde_json::from_str::<TaskDeletionOrCancelationQueryRaw>(json).unwrap_err(); let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
snapshot!(format!("{err}"), @"unknown field `limit` at line 1 column 16"); snapshot!(format!("{err}"), @"Json deserialize error: unknown field `limit`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.");
} }
} }
} }

View File

@ -11,7 +11,7 @@ async fn task_bad_uids() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task uid `doggo` is invalid. It should only contain numeric characters.", "message": "invalid digit found in string at `.uids`.",
"code": "invalid_task_uids", "code": "invalid_task_uids",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-uids" "link": "https://docs.meilisearch.com/errors#invalid-task-uids"
@ -22,7 +22,7 @@ async fn task_bad_uids() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task uid `doggo` is invalid. It should only contain numeric characters.", "message": "invalid digit found in string at `.uids`.",
"code": "invalid_task_uids", "code": "invalid_task_uids",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-uids" "link": "https://docs.meilisearch.com/errors#invalid-task-uids"
@ -33,7 +33,7 @@ async fn task_bad_uids() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task uid `doggo` is invalid. It should only contain numeric characters.", "message": "invalid digit found in string at `.uids`.",
"code": "invalid_task_uids", "code": "invalid_task_uids",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-uids" "link": "https://docs.meilisearch.com/errors#invalid-task-uids"
@ -49,7 +49,7 @@ async fn task_bad_canceled_by() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task canceledBy `doggo` is invalid. It should only contains numeric characters separated by `,` character.", "message": "invalid digit found in string at `.canceledBy`.",
"code": "invalid_task_canceled_by", "code": "invalid_task_canceled_by",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-canceled-by" "link": "https://docs.meilisearch.com/errors#invalid-task-canceled-by"
@ -60,7 +60,7 @@ async fn task_bad_canceled_by() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task canceledBy `doggo` is invalid. It should only contains numeric characters separated by `,` character.", "message": "invalid digit found in string at `.canceledBy`.",
"code": "invalid_task_canceled_by", "code": "invalid_task_canceled_by",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-canceled-by" "link": "https://docs.meilisearch.com/errors#invalid-task-canceled-by"
@ -71,7 +71,7 @@ async fn task_bad_canceled_by() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task canceledBy `doggo` is invalid. It should only contains numeric characters separated by `,` character.", "message": "invalid digit found in string at `.canceledBy`.",
"code": "invalid_task_canceled_by", "code": "invalid_task_canceled_by",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-canceled-by" "link": "https://docs.meilisearch.com/errors#invalid-task-canceled-by"
@ -87,7 +87,7 @@ async fn task_bad_types() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task type `doggo` is invalid. Available task types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`", "message": "`doggo` is not a type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`. at `.types`.",
"code": "invalid_task_types", "code": "invalid_task_types",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-types" "link": "https://docs.meilisearch.com/errors#invalid-task-types"
@ -98,7 +98,7 @@ async fn task_bad_types() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task type `doggo` is invalid. Available task types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`", "message": "`doggo` is not a type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`. at `.types`.",
"code": "invalid_task_types", "code": "invalid_task_types",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-types" "link": "https://docs.meilisearch.com/errors#invalid-task-types"
@ -109,7 +109,7 @@ async fn task_bad_types() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task type `doggo` is invalid. Available task types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`", "message": "`doggo` is not a type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`. at `.types`.",
"code": "invalid_task_types", "code": "invalid_task_types",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-types" "link": "https://docs.meilisearch.com/errors#invalid-task-types"
@ -125,7 +125,7 @@ async fn task_bad_statuses() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task status `doggo` is invalid. Available task statuses are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`.", "message": "`doggo` is not a status. Available status are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. at `.statuses`.",
"code": "invalid_task_statuses", "code": "invalid_task_statuses",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-statuses" "link": "https://docs.meilisearch.com/errors#invalid-task-statuses"
@ -136,7 +136,7 @@ async fn task_bad_statuses() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task status `doggo` is invalid. Available task statuses are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`.", "message": "`doggo` is not a status. Available status are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. at `.statuses`.",
"code": "invalid_task_statuses", "code": "invalid_task_statuses",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-statuses" "link": "https://docs.meilisearch.com/errors#invalid-task-statuses"
@ -147,7 +147,7 @@ async fn task_bad_statuses() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task status `doggo` is invalid. Available task statuses are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`.", "message": "`doggo` is not a status. Available status are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. at `.statuses`.",
"code": "invalid_task_statuses", "code": "invalid_task_statuses",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-statuses" "link": "https://docs.meilisearch.com/errors#invalid-task-statuses"
@ -163,7 +163,7 @@ async fn task_bad_index_uids() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "the good doggo is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).", "message": "`the good doggo` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). at `.indexUids`.",
"code": "invalid_index_uid", "code": "invalid_index_uid",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-index-uid" "link": "https://docs.meilisearch.com/errors#invalid-index-uid"
@ -174,7 +174,7 @@ async fn task_bad_index_uids() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "the good doggo is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).", "message": "`the good doggo` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). at `.indexUids`.",
"code": "invalid_index_uid", "code": "invalid_index_uid",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-index-uid" "link": "https://docs.meilisearch.com/errors#invalid-index-uid"
@ -185,7 +185,7 @@ async fn task_bad_index_uids() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "the good doggo is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).", "message": "`the good doggo` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). at `.indexUids`.",
"code": "invalid_index_uid", "code": "invalid_index_uid",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-index-uid" "link": "https://docs.meilisearch.com/errors#invalid-index-uid"
@ -201,10 +201,10 @@ async fn task_bad_limit() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Query deserialize error: invalid digit found in string", "message": "invalid digit found in string at `.limit`.",
"code": "bad_request", "code": "invalid_task_limit",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#bad-request" "link": "https://docs.meilisearch.com/errors#invalid-task-limit"
} }
"###); "###);
@ -212,7 +212,7 @@ async fn task_bad_limit() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Query deserialize error: unknown field `limit`", "message": "Json deserialize error: unknown field `limit`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
"code": "bad_request", "code": "bad_request",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#bad-request" "link": "https://docs.meilisearch.com/errors#bad-request"
@ -223,7 +223,7 @@ async fn task_bad_limit() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Query deserialize error: unknown field `limit`", "message": "Json deserialize error: unknown field `limit`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
"code": "bad_request", "code": "bad_request",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#bad-request" "link": "https://docs.meilisearch.com/errors#bad-request"
@ -239,10 +239,10 @@ async fn task_bad_from() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Query deserialize error: invalid digit found in string", "message": "invalid digit found in string at `.from`.",
"code": "bad_request", "code": "invalid_task_from",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#bad-request" "link": "https://docs.meilisearch.com/errors#invalid-task-from"
} }
"###); "###);
@ -250,7 +250,7 @@ async fn task_bad_from() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Query deserialize error: unknown field `from`", "message": "Json deserialize error: unknown field `from`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
"code": "bad_request", "code": "bad_request",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#bad-request" "link": "https://docs.meilisearch.com/errors#bad-request"
@ -261,7 +261,7 @@ async fn task_bad_from() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Query deserialize error: unknown field `from`", "message": "Json deserialize error: unknown field `from`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
"code": "bad_request", "code": "bad_request",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#bad-request" "link": "https://docs.meilisearch.com/errors#bad-request"
@ -277,7 +277,7 @@ async fn task_bad_after_enqueued_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `afterEnqueuedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterEnqueuedAt`.",
"code": "invalid_task_after_enqueued_at", "code": "invalid_task_after_enqueued_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-after-enqueued-at" "link": "https://docs.meilisearch.com/errors#invalid-task-after-enqueued-at"
@ -288,7 +288,7 @@ async fn task_bad_after_enqueued_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `afterEnqueuedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterEnqueuedAt`.",
"code": "invalid_task_after_enqueued_at", "code": "invalid_task_after_enqueued_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-after-enqueued-at" "link": "https://docs.meilisearch.com/errors#invalid-task-after-enqueued-at"
@ -299,7 +299,7 @@ async fn task_bad_after_enqueued_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `afterEnqueuedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterEnqueuedAt`.",
"code": "invalid_task_after_enqueued_at", "code": "invalid_task_after_enqueued_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-after-enqueued-at" "link": "https://docs.meilisearch.com/errors#invalid-task-after-enqueued-at"
@ -315,7 +315,7 @@ async fn task_bad_before_enqueued_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `beforeEnqueuedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeEnqueuedAt`.",
"code": "invalid_task_before_enqueued_at", "code": "invalid_task_before_enqueued_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-before-enqueued-at" "link": "https://docs.meilisearch.com/errors#invalid-task-before-enqueued-at"
@ -326,7 +326,7 @@ async fn task_bad_before_enqueued_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `beforeEnqueuedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeEnqueuedAt`.",
"code": "invalid_task_before_enqueued_at", "code": "invalid_task_before_enqueued_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-before-enqueued-at" "link": "https://docs.meilisearch.com/errors#invalid-task-before-enqueued-at"
@ -337,7 +337,7 @@ async fn task_bad_before_enqueued_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `beforeEnqueuedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeEnqueuedAt`.",
"code": "invalid_task_before_enqueued_at", "code": "invalid_task_before_enqueued_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-before-enqueued-at" "link": "https://docs.meilisearch.com/errors#invalid-task-before-enqueued-at"
@ -353,7 +353,7 @@ async fn task_bad_after_started_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `afterStartedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterStartedAt`.",
"code": "invalid_task_after_started_at", "code": "invalid_task_after_started_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-after-started-at" "link": "https://docs.meilisearch.com/errors#invalid-task-after-started-at"
@ -364,7 +364,7 @@ async fn task_bad_after_started_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `afterStartedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterStartedAt`.",
"code": "invalid_task_after_started_at", "code": "invalid_task_after_started_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-after-started-at" "link": "https://docs.meilisearch.com/errors#invalid-task-after-started-at"
@ -375,7 +375,7 @@ async fn task_bad_after_started_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `afterStartedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterStartedAt`.",
"code": "invalid_task_after_started_at", "code": "invalid_task_after_started_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-after-started-at" "link": "https://docs.meilisearch.com/errors#invalid-task-after-started-at"
@ -391,7 +391,7 @@ async fn task_bad_before_started_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `beforeStartedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeStartedAt`.",
"code": "invalid_task_before_started_at", "code": "invalid_task_before_started_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-before-started-at" "link": "https://docs.meilisearch.com/errors#invalid-task-before-started-at"
@ -402,7 +402,7 @@ async fn task_bad_before_started_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `beforeStartedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeStartedAt`.",
"code": "invalid_task_before_started_at", "code": "invalid_task_before_started_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-before-started-at" "link": "https://docs.meilisearch.com/errors#invalid-task-before-started-at"
@ -413,7 +413,7 @@ async fn task_bad_before_started_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `beforeStartedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeStartedAt`.",
"code": "invalid_task_before_started_at", "code": "invalid_task_before_started_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-before-started-at" "link": "https://docs.meilisearch.com/errors#invalid-task-before-started-at"
@ -429,7 +429,7 @@ async fn task_bad_after_finished_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `afterFinishedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterFinishedAt`.",
"code": "invalid_task_after_finished_at", "code": "invalid_task_after_finished_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-after-finished-at" "link": "https://docs.meilisearch.com/errors#invalid-task-after-finished-at"
@ -440,7 +440,7 @@ async fn task_bad_after_finished_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `afterFinishedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterFinishedAt`.",
"code": "invalid_task_after_finished_at", "code": "invalid_task_after_finished_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-after-finished-at" "link": "https://docs.meilisearch.com/errors#invalid-task-after-finished-at"
@ -451,7 +451,7 @@ async fn task_bad_after_finished_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `afterFinishedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterFinishedAt`.",
"code": "invalid_task_after_finished_at", "code": "invalid_task_after_finished_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-after-finished-at" "link": "https://docs.meilisearch.com/errors#invalid-task-after-finished-at"
@ -467,7 +467,7 @@ async fn task_bad_before_finished_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `beforeFinishedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeFinishedAt`.",
"code": "invalid_task_before_finished_at", "code": "invalid_task_before_finished_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-before-finished-at" "link": "https://docs.meilisearch.com/errors#invalid-task-before-finished-at"
@ -478,7 +478,7 @@ async fn task_bad_before_finished_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `beforeFinishedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeFinishedAt`.",
"code": "invalid_task_before_finished_at", "code": "invalid_task_before_finished_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-before-finished-at" "link": "https://docs.meilisearch.com/errors#invalid-task-before-finished-at"
@ -489,7 +489,7 @@ async fn task_bad_before_finished_at() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Task `beforeFinishedAt` `doggo` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeFinishedAt`.",
"code": "invalid_task_before_finished_at", "code": "invalid_task_before_finished_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-before-finished-at" "link": "https://docs.meilisearch.com/errors#invalid-task-before-finished-at"

View File

@ -183,7 +183,7 @@ async fn get_task_filter_error() {
assert_eq!(code, 400, "{}", response); assert_eq!(code, 400, "{}", response);
meili_snap::snapshot!(meili_snap::json_string!(response), @r###" meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
{ {
"message": "Query deserialize error: unknown field `lol`", "message": "Json deserialize error: unknown field `lol`, expected one of `limit`, `from`, `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
"code": "bad_request", "code": "bad_request",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#bad-request" "link": "https://docs.meilisearch.com/errors#bad-request"
@ -194,7 +194,7 @@ async fn get_task_filter_error() {
assert_eq!(code, 400, "{}", response); assert_eq!(code, 400, "{}", response);
meili_snap::snapshot!(meili_snap::json_string!(response), @r###" meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
{ {
"message": "Task uid `pied` is invalid. It should only contain numeric characters.", "message": "invalid digit found in string at `.uids`.",
"code": "invalid_task_uids", "code": "invalid_task_uids",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-uids" "link": "https://docs.meilisearch.com/errors#invalid-task-uids"
@ -205,10 +205,10 @@ async fn get_task_filter_error() {
assert_eq!(code, 400, "{}", response); assert_eq!(code, 400, "{}", response);
meili_snap::snapshot!(meili_snap::json_string!(response), @r###" meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
{ {
"message": "Query deserialize error: invalid digit found in string", "message": "invalid digit found in string at `.from`.",
"code": "bad_request", "code": "invalid_task_from",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#bad-request" "link": "https://docs.meilisearch.com/errors#invalid-task-from"
} }
"###); "###);
@ -216,7 +216,7 @@ async fn get_task_filter_error() {
assert_eq!(code, 400, "{}", response); assert_eq!(code, 400, "{}", response);
meili_snap::snapshot!(meili_snap::json_string!(response), @r###" meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
{ {
"message": "Task `beforeStartedAt` `pied` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", "message": "`pied` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeStartedAt`.",
"code": "invalid_task_before_started_at", "code": "invalid_task_before_started_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-before-started-at" "link": "https://docs.meilisearch.com/errors#invalid-task-before-started-at"
@ -243,7 +243,7 @@ async fn delete_task_filter_error() {
assert_eq!(code, 400, "{}", response); assert_eq!(code, 400, "{}", response);
meili_snap::snapshot!(meili_snap::json_string!(response), @r###" meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
{ {
"message": "Query deserialize error: unknown field `lol`", "message": "Json deserialize error: unknown field `lol`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
"code": "bad_request", "code": "bad_request",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#bad-request" "link": "https://docs.meilisearch.com/errors#bad-request"
@ -254,7 +254,7 @@ async fn delete_task_filter_error() {
assert_eq!(code, 400, "{}", response); assert_eq!(code, 400, "{}", response);
meili_snap::snapshot!(meili_snap::json_string!(response), @r###" meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
{ {
"message": "Task uid `pied` is invalid. It should only contain numeric characters.", "message": "invalid digit found in string at `.uids`.",
"code": "invalid_task_uids", "code": "invalid_task_uids",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-uids" "link": "https://docs.meilisearch.com/errors#invalid-task-uids"
@ -281,7 +281,7 @@ async fn cancel_task_filter_error() {
assert_eq!(code, 400, "{}", response); assert_eq!(code, 400, "{}", response);
meili_snap::snapshot!(meili_snap::json_string!(response), @r###" meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
{ {
"message": "Query deserialize error: unknown field `lol`", "message": "Json deserialize error: unknown field `lol`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
"code": "bad_request", "code": "bad_request",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#bad-request" "link": "https://docs.meilisearch.com/errors#bad-request"
@ -292,7 +292,7 @@ async fn cancel_task_filter_error() {
assert_eq!(code, 400, "{}", response); assert_eq!(code, 400, "{}", response);
meili_snap::snapshot!(meili_snap::json_string!(response), @r###" meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
{ {
"message": "Task uid `pied` is invalid. It should only contain numeric characters.", "message": "invalid digit found in string at `.uids`.",
"code": "invalid_task_uids", "code": "invalid_task_uids",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-uids" "link": "https://docs.meilisearch.com/errors#invalid-task-uids"