Improve error messages generated by deserr

Split Json and Query Parameter error types
This commit is contained in:
Loïc Lecrenier 2023-01-12 13:55:53 +01:00
parent a5c4fbbcea
commit 436ae4e466
25 changed files with 802 additions and 448 deletions

2
Cargo.lock generated
View File

@ -2348,7 +2348,6 @@ dependencies = [
"rustls-pemfile", "rustls-pemfile",
"segment", "segment",
"serde", "serde",
"serde-cs",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"sha-1", "sha-1",
@ -2413,6 +2412,7 @@ dependencies = [
"proptest-derive", "proptest-derive",
"roaring", "roaring",
"serde", "serde",
"serde-cs",
"serde_json", "serde_json",
"tar", "tar",
"tempfile", "tempfile",

View File

@ -21,6 +21,7 @@ proptest = { version = "1.0.0", optional = true }
proptest-derive = { version = "0.3.0", optional = true } proptest-derive = { version = "0.3.0", optional = true }
roaring = { version = "0.10.0", features = ["serde"] } roaring = { version = "0.10.0", features = ["serde"] }
serde = { version = "1.0.145", features = ["derive"] } serde = { version = "1.0.145", features = ["derive"] }
serde-cs = "0.2.4"
serde_json = "1.0.85" serde_json = "1.0.85"
tar = "0.4.38" tar = "0.4.38"
tempfile = "3.3.0" tempfile = "3.3.0"

View File

@ -1,14 +1,18 @@
use std::convert::Infallible; use std::convert::Infallible;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::str::FromStr;
use std::{fmt, io}; use std::{fmt, io};
use actix_web::http::StatusCode; use actix_web::http::StatusCode;
use actix_web::{self as aweb, HttpResponseBuilder}; use actix_web::{self as aweb, HttpResponseBuilder};
use aweb::rt::task::JoinError; use aweb::rt::task::JoinError;
use convert_case::Casing; use convert_case::Casing;
use deserr::{DeserializeError, IntoValue, MergeWithError, ValuePointerRef}; use deserr::{DeserializeError, ErrorKind, IntoValue, MergeWithError, ValueKind, ValuePointerRef};
use milli::heed::{Error as HeedError, MdbError}; use milli::heed::{Error as HeedError, MdbError};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_cs::vec::CS;
use crate::star_or::StarOr;
use self::deserr_codes::MissingIndexUid; use self::deserr_codes::MissingIndexUid;
@ -422,41 +426,49 @@ mod strategy {
} }
} }
pub struct DeserrError<C: ErrorCode = deserr_codes::BadRequest> { pub struct DeserrJson;
pub struct DeserrQueryParam;
pub type DeserrJsonError<C = deserr_codes::BadRequest> = DeserrError<DeserrJson, C>;
pub type DeserrQueryParamError<C = deserr_codes::BadRequest> = DeserrError<DeserrQueryParam, C>;
pub struct DeserrError<Format, C: Default + ErrorCode> {
pub msg: String, pub msg: String,
pub code: Code, pub code: Code,
_phantom: PhantomData<C>, _phantom: PhantomData<(Format, C)>,
} }
impl<C: ErrorCode> std::fmt::Debug for DeserrError<C> { impl<Format, C: Default + ErrorCode> std::fmt::Debug for DeserrError<Format, C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DeserrError").field("msg", &self.msg).field("code", &self.code).finish() f.debug_struct("DeserrError").field("msg", &self.msg).field("code", &self.code).finish()
} }
} }
impl<C: ErrorCode> std::fmt::Display for DeserrError<C> { impl<Format, C: Default + ErrorCode> std::fmt::Display for DeserrError<Format, C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.msg) write!(f, "{}", self.msg)
} }
} }
impl<C: ErrorCode> std::error::Error for DeserrError<C> {} impl<Format, C: Default + ErrorCode> std::error::Error for DeserrError<Format, C> {}
impl<C: ErrorCode> ErrorCode for DeserrError<C> { impl<Format, C: Default + ErrorCode> ErrorCode for DeserrError<Format, C> {
fn error_code(&self) -> Code { fn error_code(&self) -> Code {
self.code self.code
} }
} }
impl<C1: ErrorCode, C2: ErrorCode> MergeWithError<DeserrError<C2>> for DeserrError<C1> { impl<Format, C1: Default + ErrorCode, C2: Default + ErrorCode>
MergeWithError<DeserrError<Format, C2>> for DeserrError<Format, C1>
{
fn merge( fn merge(
_self_: Option<Self>, _self_: Option<Self>,
other: DeserrError<C2>, other: DeserrError<Format, C2>,
_merge_location: ValuePointerRef, _merge_location: ValuePointerRef,
) -> Result<Self, Self> { ) -> Result<Self, Self> {
Err(DeserrError { msg: other.msg, code: other.code, _phantom: PhantomData }) Err(DeserrError { msg: other.msg, code: other.code, _phantom: PhantomData })
} }
} }
impl DeserrError<MissingIndexUid> { impl DeserrJsonError<MissingIndexUid> {
pub fn missing_index_uid(field: &str, location: ValuePointerRef) -> Self { pub fn missing_index_uid(field: &str, location: ValuePointerRef) -> Self {
let x = unwrap_any(Self::error::<Infallible>( let x = unwrap_any(Self::error::<Infallible>(
None, None,
@ -467,21 +479,364 @@ impl DeserrError<MissingIndexUid> {
} }
} }
impl<C: Default + ErrorCode> deserr::DeserializeError for DeserrError<C> { // if the error happened in the root, then an empty string is returned.
pub fn location_json_description(location: ValuePointerRef, article: &str) -> String {
fn rec(location: ValuePointerRef) -> String {
match location {
ValuePointerRef::Origin => String::new(),
ValuePointerRef::Key { key, prev } => rec(*prev) + "." + key,
ValuePointerRef::Index { index, prev } => format!("{}[{index}]", rec(*prev)),
}
}
match location {
ValuePointerRef::Origin => String::new(),
_ => {
format!("{article} `{}`", rec(location))
}
}
}
fn value_kinds_description_json(kinds: &[ValueKind]) -> String {
fn order(kind: &ValueKind) -> u8 {
match kind {
ValueKind::Null => 0,
ValueKind::Boolean => 1,
ValueKind::Integer => 2,
ValueKind::NegativeInteger => 3,
ValueKind::Float => 4,
ValueKind::String => 5,
ValueKind::Sequence => 6,
ValueKind::Map => 7,
}
}
fn single_description(kind: &ValueKind) -> &'static str {
match kind {
ValueKind::Null => "null",
ValueKind::Boolean => "a boolean",
ValueKind::Integer => "a positive integer",
ValueKind::NegativeInteger => "an integer",
ValueKind::Float => "a number",
ValueKind::String => "a string",
ValueKind::Sequence => "an array",
ValueKind::Map => "an object",
}
}
fn description_rec(kinds: &[ValueKind], count_items: &mut usize, message: &mut String) {
let (msg_part, rest): (_, &[ValueKind]) = match kinds {
[] => (String::new(), &[]),
[ValueKind::Integer | ValueKind::NegativeInteger, ValueKind::Float, rest @ ..] => {
("a number".to_owned(), rest)
}
[ValueKind::Integer, ValueKind::NegativeInteger, ValueKind::Float, rest @ ..] => {
("a number".to_owned(), rest)
}
[ValueKind::Integer, ValueKind::NegativeInteger, rest @ ..] => {
("an integer".to_owned(), rest)
}
[a] => (single_description(a).to_owned(), &[]),
[a, rest @ ..] => (single_description(a).to_owned(), rest),
};
if rest.is_empty() {
if *count_items == 0 {
message.push_str(&msg_part);
} else if *count_items == 1 {
message.push_str(&format!(" or {msg_part}"));
} else {
message.push_str(&format!(", or {msg_part}"));
}
} else {
if *count_items == 0 {
message.push_str(&msg_part);
} else {
message.push_str(&format!(", {msg_part}"));
}
*count_items += 1;
description_rec(rest, count_items, message);
}
}
let mut kinds = kinds.to_owned();
kinds.sort_by_key(order);
kinds.dedup();
if kinds.is_empty() {
"a different value".to_owned()
} else {
let mut message = String::new();
description_rec(kinds.as_slice(), &mut 0, &mut message);
message
}
}
fn value_description_with_kind_json(v: &serde_json::Value) -> String {
match v.kind() {
ValueKind::Null => "null".to_owned(),
kind => {
format!(
"{}: `{}`",
value_kinds_description_json(&[kind]),
serde_json::to_string(v).unwrap()
)
}
}
}
impl<C: Default + ErrorCode> deserr::DeserializeError for DeserrJsonError<C> {
fn error<V: IntoValue>( fn error<V: IntoValue>(
_self_: Option<Self>, _self_: Option<Self>,
error: deserr::ErrorKind<V>, error: deserr::ErrorKind<V>,
location: ValuePointerRef, location: ValuePointerRef,
) -> Result<Self, Self> { ) -> Result<Self, Self> {
let msg = unwrap_any(deserr::serde_json::JsonError::error(None, error, location)).0; let mut message = String::new();
Err(DeserrError { msg, code: C::default().error_code(), _phantom: PhantomData }) message.push_str(&match error {
ErrorKind::IncorrectValueKind { actual, accepted } => {
let expected = value_kinds_description_json(accepted);
// if we're not able to get the value as a string then we print nothing.
let received = value_description_with_kind_json(&serde_json::Value::from(actual));
let location = location_json_description(location, " at");
format!("Invalid value type{location}: expected {expected}, but found {received}")
}
ErrorKind::MissingField { field } => {
// serde_json original message:
// Json deserialize error: missing field `lol` at line 1 column 2
let location = location_json_description(location, " inside");
format!("Missing field `{field}`{location}")
}
ErrorKind::UnknownKey { key, accepted } => {
let location = location_json_description(location, " inside");
format!(
"Unknown field `{}`{location}: expected one of {}",
key,
accepted
.iter()
.map(|accepted| format!("`{}`", accepted))
.collect::<Vec<String>>()
.join(", ")
)
}
ErrorKind::UnknownValue { value, accepted } => {
let location = location_json_description(location, " at");
format!(
"Unknown value `{}`{location}: expected one of {}",
value,
accepted
.iter()
.map(|accepted| format!("`{}`", accepted))
.collect::<Vec<String>>()
.join(", "),
)
}
ErrorKind::Unexpected { msg } => {
let location = location_json_description(location, " at");
// serde_json original message:
// The json payload provided is malformed. `trailing characters at line 1 column 19`.
format!("Invalid value{location}: {msg}")
}
});
Err(DeserrJsonError {
msg: message,
code: C::default().error_code(),
_phantom: PhantomData,
})
} }
} }
// if the error happened in the root, then an empty string is returned.
pub fn location_query_param_description(location: ValuePointerRef, article: &str) -> String {
fn rec(location: ValuePointerRef) -> String {
match location {
ValuePointerRef::Origin => String::new(),
ValuePointerRef::Key { key, prev } => {
if matches!(prev, ValuePointerRef::Origin) {
key.to_owned()
} else {
rec(*prev) + "." + key
}
}
ValuePointerRef::Index { index, prev } => format!("{}[{index}]", rec(*prev)),
}
}
match location {
ValuePointerRef::Origin => String::new(),
_ => {
format!("{article} `{}`", rec(location))
}
}
}
impl<C: Default + ErrorCode> deserr::DeserializeError for DeserrQueryParamError<C> {
fn error<V: IntoValue>(
_self_: Option<Self>,
error: deserr::ErrorKind<V>,
location: ValuePointerRef,
) -> Result<Self, Self> {
let mut message = String::new();
message.push_str(&match error {
ErrorKind::IncorrectValueKind { actual, accepted } => {
let expected = value_kinds_description_query_param(accepted);
// if we're not able to get the value as a string then we print nothing.
let received = value_description_with_kind_query_param(actual);
let location = location_query_param_description(location, " for parameter");
format!("Invalid value type{location}: expected {expected}, but found {received}")
}
ErrorKind::MissingField { field } => {
// serde_json original message:
// Json deserialize error: missing field `lol` at line 1 column 2
let location = location_query_param_description(location, " inside");
format!("Missing parameter `{field}`{location}")
}
ErrorKind::UnknownKey { key, accepted } => {
let location = location_query_param_description(location, " inside");
format!(
"Unknown parameter `{}`{location}: expected one of {}",
key,
accepted
.iter()
.map(|accepted| format!("`{}`", accepted))
.collect::<Vec<String>>()
.join(", ")
)
}
ErrorKind::UnknownValue { value, accepted } => {
let location = location_query_param_description(location, " for parameter");
format!(
"Unknown value `{}`{location}: expected one of {}",
value,
accepted
.iter()
.map(|accepted| format!("`{}`", accepted))
.collect::<Vec<String>>()
.join(", "),
)
}
ErrorKind::Unexpected { msg } => {
let location = location_query_param_description(location, " in parameter");
// serde_json original message:
// The json payload provided is malformed. `trailing characters at line 1 column 19`.
format!("Invalid value{location}: {msg}")
}
});
Err(DeserrQueryParamError {
msg: message,
code: C::default().error_code(),
_phantom: PhantomData,
})
}
}
fn value_kinds_description_query_param(_accepted: &[ValueKind]) -> String {
"a string".to_owned()
}
fn value_description_with_kind_query_param<V: IntoValue>(actual: deserr::Value<V>) -> String {
match actual {
deserr::Value::Null => "null".to_owned(),
deserr::Value::Boolean(x) => format!("a boolean: `{x}`"),
deserr::Value::Integer(x) => format!("an integer: `{x}`"),
deserr::Value::NegativeInteger(x) => {
format!("an integer: `{x}`")
}
deserr::Value::Float(x) => {
format!("a number: `{x}`")
}
deserr::Value::String(x) => {
format!("a string: `{x}`")
}
deserr::Value::Sequence(_) => "multiple values".to_owned(),
deserr::Value::Map(_) => "multiple parameters".to_owned(),
}
}
#[derive(Debug)]
pub struct DetailedParseIntError(String);
impl fmt::Display for DetailedParseIntError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "could not parse `{}` as a positive integer", self.0)
}
}
impl std::error::Error for DetailedParseIntError {}
pub fn parse_u32_query_param(x: String) -> Result<u32, TakeErrorMessage<DetailedParseIntError>> {
x.parse::<u32>().map_err(|_e| TakeErrorMessage(DetailedParseIntError(x.to_owned())))
}
pub fn parse_usize_query_param(
x: String,
) -> Result<usize, TakeErrorMessage<DetailedParseIntError>> {
x.parse::<usize>().map_err(|_e| TakeErrorMessage(DetailedParseIntError(x.to_owned())))
}
pub fn parse_option_usize_query_param(
s: Option<String>,
) -> Result<Option<usize>, TakeErrorMessage<DetailedParseIntError>> {
if let Some(s) = s {
parse_usize_query_param(s).map(Some)
} else {
Ok(None)
}
}
pub fn parse_option_u32_query_param(
s: Option<String>,
) -> Result<Option<u32>, TakeErrorMessage<DetailedParseIntError>> {
if let Some(s) = s {
parse_u32_query_param(s).map(Some)
} else {
Ok(None)
}
}
pub fn parse_option_vec_u32_query_param(
s: Option<serde_cs::vec::CS<String>>,
) -> Result<Option<Vec<u32>>, TakeErrorMessage<DetailedParseIntError>> {
if let Some(s) = s {
s.into_iter()
.map(parse_u32_query_param)
.collect::<Result<Vec<u32>, TakeErrorMessage<DetailedParseIntError>>>()
.map(Some)
} else {
Ok(None)
}
}
pub fn parse_option_cs_star_or<T: FromStr>(
s: Option<CS<StarOr<String>>>,
) -> 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 {
Ok(None)
}
}
/// Extracts the raw values from the `StarOr` types and
/// return None if a `StarOr::Star` is encountered.
pub fn fold_star_or<T, O>(content: impl IntoIterator<Item = StarOr<T>>) -> Option<O>
where
O: FromIterator<T>,
{
content
.into_iter()
.map(|value| match value {
StarOr::Star => None,
StarOr::Other(val) => Some(val),
})
.collect()
}
pub struct TakeErrorMessage<T>(pub T); pub struct TakeErrorMessage<T>(pub T);
impl<C: Default + ErrorCode, T> MergeWithError<TakeErrorMessage<T>> for DeserrError<C> impl<C: Default + ErrorCode, T> MergeWithError<TakeErrorMessage<T>> for DeserrJsonError<C>
where where
T: std::error::Error, T: std::error::Error,
{ {
@ -490,7 +845,24 @@ where
other: TakeErrorMessage<T>, other: TakeErrorMessage<T>,
merge_location: ValuePointerRef, merge_location: ValuePointerRef,
) -> Result<Self, Self> { ) -> Result<Self, Self> {
DeserrError::error::<Infallible>( DeserrJsonError::error::<Infallible>(
None,
deserr::ErrorKind::Unexpected { msg: other.0.to_string() },
merge_location,
)
}
}
impl<C: Default + ErrorCode, T> MergeWithError<TakeErrorMessage<T>> for DeserrQueryParamError<C>
where
T: std::error::Error,
{
fn merge(
_self_: Option<Self>,
other: TakeErrorMessage<T>,
merge_location: ValuePointerRef,
) -> Result<Self, Self> {
DeserrQueryParamError::error::<Infallible>(
None, None,
deserr::ErrorKind::Unexpected { msg: other.0.to_string() }, deserr::ErrorKind::Unexpected { msg: other.0.to_string() },
merge_location, merge_location,
@ -510,3 +882,32 @@ macro_rules! internal_error {
)* )*
} }
} }
#[cfg(test)]
mod tests {
use deserr::ValueKind;
use crate::error::value_kinds_description_json;
#[test]
fn test_value_kinds_description_json() {
insta::assert_display_snapshot!(value_kinds_description_json(&[]), @"a different value");
insta::assert_display_snapshot!(value_kinds_description_json(&[ValueKind::Boolean]), @"a boolean");
insta::assert_display_snapshot!(value_kinds_description_json(&[ValueKind::Integer]), @"a positive integer");
insta::assert_display_snapshot!(value_kinds_description_json(&[ValueKind::NegativeInteger]), @"an integer");
insta::assert_display_snapshot!(value_kinds_description_json(&[ValueKind::Integer]), @"a positive integer");
insta::assert_display_snapshot!(value_kinds_description_json(&[ValueKind::String]), @"a string");
insta::assert_display_snapshot!(value_kinds_description_json(&[ValueKind::Sequence]), @"an array");
insta::assert_display_snapshot!(value_kinds_description_json(&[ValueKind::Map]), @"an object");
insta::assert_display_snapshot!(value_kinds_description_json(&[ValueKind::Integer, ValueKind::Boolean]), @"a boolean or a positive integer");
insta::assert_display_snapshot!(value_kinds_description_json(&[ValueKind::Null, ValueKind::Integer]), @"null or a positive integer");
insta::assert_display_snapshot!(value_kinds_description_json(&[ValueKind::Sequence, ValueKind::NegativeInteger]), @"an integer or an array");
insta::assert_display_snapshot!(value_kinds_description_json(&[ValueKind::Integer, ValueKind::Float]), @"a number");
insta::assert_display_snapshot!(value_kinds_description_json(&[ValueKind::Integer, ValueKind::Float, ValueKind::NegativeInteger]), @"a number");
insta::assert_display_snapshot!(value_kinds_description_json(&[ValueKind::Integer, ValueKind::Float, ValueKind::NegativeInteger, ValueKind::Null]), @"null or a number");
insta::assert_display_snapshot!(value_kinds_description_json(&[ValueKind::Boolean, ValueKind::Integer, ValueKind::Float, ValueKind::NegativeInteger, ValueKind::Null]), @"null, a boolean, or a number");
insta::assert_display_snapshot!(value_kinds_description_json(&[ValueKind::Null, ValueKind::Boolean, ValueKind::Integer, ValueKind::Float, ValueKind::NegativeInteger, ValueKind::Null]), @"null, a boolean, or a number");
}
}

View File

@ -11,19 +11,19 @@ use time::{Date, OffsetDateTime, PrimitiveDateTime};
use uuid::Uuid; use uuid::Uuid;
use crate::error::deserr_codes::*; use crate::error::deserr_codes::*;
use crate::error::{unwrap_any, Code, DeserrError, ErrorCode, TakeErrorMessage}; use crate::error::{unwrap_any, Code, DeserrJsonError, ErrorCode, TakeErrorMessage};
use crate::index_uid::{IndexUid, IndexUidFormatError}; use crate::index_uid::{IndexUid, IndexUidFormatError};
use crate::star_or::StarOr; use crate::star_or::StarOr;
pub type KeyId = Uuid; pub type KeyId = Uuid;
impl<C: Default + ErrorCode> MergeWithError<IndexUidFormatError> for DeserrError<C> { impl<C: Default + ErrorCode> MergeWithError<IndexUidFormatError> for DeserrJsonError<C> {
fn merge( fn merge(
_self_: Option<Self>, _self_: Option<Self>,
other: IndexUidFormatError, other: IndexUidFormatError,
merge_location: deserr::ValuePointerRef, merge_location: deserr::ValuePointerRef,
) -> std::result::Result<Self, Self> { ) -> std::result::Result<Self, Self> {
DeserrError::error::<Infallible>( DeserrJsonError::error::<Infallible>(
None, None,
deserr::ErrorKind::Unexpected { msg: other.to_string() }, deserr::ErrorKind::Unexpected { msg: other.to_string() },
merge_location, merge_location,
@ -36,19 +36,19 @@ fn parse_uuid_from_str(s: &str) -> Result<Uuid, TakeErrorMessage<uuid::Error>> {
} }
#[derive(Debug, DeserializeFromValue)] #[derive(Debug, DeserializeFromValue)]
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)] #[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)]
pub struct CreateApiKey { pub struct CreateApiKey {
#[deserr(error = DeserrError<InvalidApiKeyDescription>)] #[deserr(error = DeserrJsonError<InvalidApiKeyDescription>)]
pub description: Option<String>, pub description: Option<String>,
#[deserr(error = DeserrError<InvalidApiKeyName>)] #[deserr(error = DeserrJsonError<InvalidApiKeyName>)]
pub name: Option<String>, pub name: Option<String>,
#[deserr(default = Uuid::new_v4(), error = DeserrError<InvalidApiKeyUid>, from(&String) = parse_uuid_from_str -> TakeErrorMessage<uuid::Error>)] #[deserr(default = Uuid::new_v4(), error = DeserrJsonError<InvalidApiKeyUid>, from(&String) = parse_uuid_from_str -> TakeErrorMessage<uuid::Error>)]
pub uid: KeyId, pub uid: KeyId,
#[deserr(error = DeserrError<InvalidApiKeyActions>)] #[deserr(error = DeserrJsonError<InvalidApiKeyActions>)]
pub actions: Vec<Action>, pub actions: Vec<Action>,
#[deserr(error = DeserrError<InvalidApiKeyIndexes>)] #[deserr(error = DeserrJsonError<InvalidApiKeyIndexes>)]
pub indexes: Vec<StarOr<IndexUid>>, pub indexes: Vec<StarOr<IndexUid>>,
#[deserr(error = DeserrError<InvalidApiKeyExpiresAt>, default = None, from(&String) = parse_expiration_date -> TakeErrorMessage<ParseOffsetDateTimeError>)] #[deserr(error = DeserrJsonError<InvalidApiKeyExpiresAt>, default = None, from(&String) = parse_expiration_date -> TakeErrorMessage<ParseOffsetDateTimeError>)]
pub expires_at: Option<OffsetDateTime>, pub expires_at: Option<OffsetDateTime>,
} }
impl CreateApiKey { impl CreateApiKey {
@ -72,8 +72,8 @@ fn deny_immutable_fields_api_key(
field: &str, field: &str,
accepted: &[&str], accepted: &[&str],
location: ValuePointerRef, location: ValuePointerRef,
) -> DeserrError { ) -> DeserrJsonError {
let mut error = unwrap_any(DeserrError::<BadRequest>::error::<Infallible>( let mut error = unwrap_any(DeserrJsonError::<BadRequest>::error::<Infallible>(
None, None,
deserr::ErrorKind::UnknownKey { key: field, accepted }, deserr::ErrorKind::UnknownKey { key: field, accepted },
location, location,
@ -92,11 +92,11 @@ fn deny_immutable_fields_api_key(
} }
#[derive(Debug, DeserializeFromValue)] #[derive(Debug, DeserializeFromValue)]
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields = deny_immutable_fields_api_key)] #[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields = deny_immutable_fields_api_key)]
pub struct PatchApiKey { pub struct PatchApiKey {
#[deserr(error = DeserrError<InvalidApiKeyDescription>)] #[deserr(error = DeserrJsonError<InvalidApiKeyDescription>)]
pub description: Option<String>, pub description: Option<String>,
#[deserr(error = DeserrError<InvalidApiKeyName>)] #[deserr(error = DeserrJsonError<InvalidApiKeyName>)]
pub name: Option<String>, pub name: Option<String>,
} }

View File

@ -8,8 +8,10 @@ pub mod star_or;
pub mod tasks; pub mod tasks;
pub mod versioning; pub mod versioning;
pub use deserr;
pub use milli; pub use milli;
pub use milli::{heed, Index}; pub use milli::{heed, Index};
pub use serde_cs;
use uuid::Uuid; use uuid::Uuid;
pub use versioning::VERSION_FILE_NAME; pub use versioning::VERSION_FILE_NAME;

View File

@ -12,7 +12,7 @@ use milli::{Criterion, CriterionError, Index, DEFAULT_VALUES_PER_FACET};
use serde::{Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize, Serializer};
use crate::error::deserr_codes::*; use crate::error::deserr_codes::*;
use crate::error::{unwrap_any, DeserrError}; use crate::error::{unwrap_any, DeserrJsonError};
/// The maximimum number of results that the engine /// The maximimum number of results that the engine
/// will be able to return in one search call. /// will be able to return in one search call.
@ -66,7 +66,7 @@ fn validate_min_word_size_for_typo_setting<E: DeserializeError>(
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, DeserializeFromValue)] #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, DeserializeFromValue)]
#[serde(deny_unknown_fields, rename_all = "camelCase")] #[serde(deny_unknown_fields, rename_all = "camelCase")]
#[deserr(deny_unknown_fields, rename_all = camelCase, validate = validate_min_word_size_for_typo_setting -> DeserrError<InvalidMinWordLengthForTypo>)] #[deserr(deny_unknown_fields, rename_all = camelCase, validate = validate_min_word_size_for_typo_setting -> DeserrJsonError<InvalidMinWordLengthForTypo>)]
pub struct MinWordSizeTyposSetting { pub struct MinWordSizeTyposSetting {
#[serde(default, skip_serializing_if = "Setting::is_not_set")] #[serde(default, skip_serializing_if = "Setting::is_not_set")]
pub one_typo: Setting<u8>, pub one_typo: Setting<u8>,
@ -76,12 +76,12 @@ pub struct MinWordSizeTyposSetting {
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, DeserializeFromValue)] #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, DeserializeFromValue)]
#[serde(deny_unknown_fields, rename_all = "camelCase")] #[serde(deny_unknown_fields, rename_all = "camelCase")]
#[deserr(deny_unknown_fields, rename_all = camelCase, where_predicate = __Deserr_E: deserr::MergeWithError<DeserrError<InvalidMinWordLengthForTypo>>)] #[deserr(deny_unknown_fields, rename_all = camelCase, where_predicate = __Deserr_E: deserr::MergeWithError<DeserrJsonError<InvalidMinWordLengthForTypo>>)]
pub struct TypoSettings { pub struct TypoSettings {
#[serde(default, skip_serializing_if = "Setting::is_not_set")] #[serde(default, skip_serializing_if = "Setting::is_not_set")]
pub enabled: Setting<bool>, pub enabled: Setting<bool>,
#[serde(default, skip_serializing_if = "Setting::is_not_set")] #[serde(default, skip_serializing_if = "Setting::is_not_set")]
#[deserr(error = DeserrError<InvalidMinWordLengthForTypo>)] #[deserr(error = DeserrJsonError<InvalidMinWordLengthForTypo>)]
pub min_word_size_for_typos: Setting<MinWordSizeTyposSetting>, pub min_word_size_for_typos: Setting<MinWordSizeTyposSetting>,
#[serde(default, skip_serializing_if = "Setting::is_not_set")] #[serde(default, skip_serializing_if = "Setting::is_not_set")]
pub disable_on_words: Setting<BTreeSet<String>>, pub disable_on_words: Setting<BTreeSet<String>>,
@ -105,7 +105,7 @@ pub struct PaginationSettings {
pub max_total_hits: Setting<usize>, pub max_total_hits: Setting<usize>,
} }
impl MergeWithError<milli::CriterionError> for DeserrError<InvalidSettingsRankingRules> { impl MergeWithError<milli::CriterionError> for DeserrJsonError<InvalidSettingsRankingRules> {
fn merge( fn merge(
_self_: Option<Self>, _self_: Option<Self>,
other: milli::CriterionError, other: milli::CriterionError,
@ -128,14 +128,14 @@ impl MergeWithError<milli::CriterionError> for DeserrError<InvalidSettingsRankin
rename_all = "camelCase", rename_all = "camelCase",
bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'static>") bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'static>")
)] )]
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)] #[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)]
pub struct Settings<T> { pub struct Settings<T> {
#[serde( #[serde(
default, default,
serialize_with = "serialize_with_wildcard", serialize_with = "serialize_with_wildcard",
skip_serializing_if = "Setting::is_not_set" skip_serializing_if = "Setting::is_not_set"
)] )]
#[deserr(error = DeserrError<InvalidSettingsDisplayedAttributes>)] #[deserr(error = DeserrJsonError<InvalidSettingsDisplayedAttributes>)]
pub displayed_attributes: Setting<Vec<String>>, pub displayed_attributes: Setting<Vec<String>>,
#[serde( #[serde(
@ -143,35 +143,35 @@ pub struct Settings<T> {
serialize_with = "serialize_with_wildcard", serialize_with = "serialize_with_wildcard",
skip_serializing_if = "Setting::is_not_set" skip_serializing_if = "Setting::is_not_set"
)] )]
#[deserr(error = DeserrError<InvalidSettingsSearchableAttributes>)] #[deserr(error = DeserrJsonError<InvalidSettingsSearchableAttributes>)]
pub searchable_attributes: Setting<Vec<String>>, pub searchable_attributes: Setting<Vec<String>>,
#[serde(default, skip_serializing_if = "Setting::is_not_set")] #[serde(default, skip_serializing_if = "Setting::is_not_set")]
#[deserr(error = DeserrError<InvalidSettingsFilterableAttributes>)] #[deserr(error = DeserrJsonError<InvalidSettingsFilterableAttributes>)]
pub filterable_attributes: Setting<BTreeSet<String>>, pub filterable_attributes: Setting<BTreeSet<String>>,
#[serde(default, skip_serializing_if = "Setting::is_not_set")] #[serde(default, skip_serializing_if = "Setting::is_not_set")]
#[deserr(error = DeserrError<InvalidSettingsSortableAttributes>)] #[deserr(error = DeserrJsonError<InvalidSettingsSortableAttributes>)]
pub sortable_attributes: Setting<BTreeSet<String>>, pub sortable_attributes: Setting<BTreeSet<String>>,
#[serde(default, skip_serializing_if = "Setting::is_not_set")] #[serde(default, skip_serializing_if = "Setting::is_not_set")]
#[deserr(error = DeserrError<InvalidSettingsRankingRules>)] #[deserr(error = DeserrJsonError<InvalidSettingsRankingRules>)]
pub ranking_rules: Setting<Vec<RankingRuleView>>, pub ranking_rules: Setting<Vec<RankingRuleView>>,
#[serde(default, skip_serializing_if = "Setting::is_not_set")] #[serde(default, skip_serializing_if = "Setting::is_not_set")]
#[deserr(error = DeserrError<InvalidSettingsStopWords>)] #[deserr(error = DeserrJsonError<InvalidSettingsStopWords>)]
pub stop_words: Setting<BTreeSet<String>>, pub stop_words: Setting<BTreeSet<String>>,
#[serde(default, skip_serializing_if = "Setting::is_not_set")] #[serde(default, skip_serializing_if = "Setting::is_not_set")]
#[deserr(error = DeserrError<InvalidSettingsSynonyms>)] #[deserr(error = DeserrJsonError<InvalidSettingsSynonyms>)]
pub synonyms: Setting<BTreeMap<String, Vec<String>>>, pub synonyms: Setting<BTreeMap<String, Vec<String>>>,
#[serde(default, skip_serializing_if = "Setting::is_not_set")] #[serde(default, skip_serializing_if = "Setting::is_not_set")]
#[deserr(error = DeserrError<InvalidSettingsDistinctAttribute>)] #[deserr(error = DeserrJsonError<InvalidSettingsDistinctAttribute>)]
pub distinct_attribute: Setting<String>, pub distinct_attribute: Setting<String>,
#[serde(default, skip_serializing_if = "Setting::is_not_set")] #[serde(default, skip_serializing_if = "Setting::is_not_set")]
#[deserr(error = DeserrError<InvalidSettingsTypoTolerance>)] #[deserr(error = DeserrJsonError<InvalidSettingsTypoTolerance>)]
pub typo_tolerance: Setting<TypoSettings>, pub typo_tolerance: Setting<TypoSettings>,
#[serde(default, skip_serializing_if = "Setting::is_not_set")] #[serde(default, skip_serializing_if = "Setting::is_not_set")]
#[deserr(error = DeserrError<InvalidSettingsFaceting>)] #[deserr(error = DeserrJsonError<InvalidSettingsFaceting>)]
pub faceting: Setting<FacetingSettings>, pub faceting: Setting<FacetingSettings>,
#[serde(default, skip_serializing_if = "Setting::is_not_set")] #[serde(default, skip_serializing_if = "Setting::is_not_set")]
#[deserr(error = DeserrError<InvalidSettingsPagination>)] #[deserr(error = DeserrJsonError<InvalidSettingsPagination>)]
pub pagination: Setting<PaginationSettings>, pub pagination: Setting<PaginationSettings>,
#[serde(skip)] #[serde(skip)]

View File

@ -348,7 +348,7 @@ impl FromStr for Status {
} else { } else {
Err(ResponseError::from_msg( Err(ResponseError::from_msg(
format!( format!(
"`{}` is not a status. Available status are {}.", "`{}` is not a valid task status. Available statuses are {}.",
status, status,
enum_iterator::all::<Status>() enum_iterator::all::<Status>()
.map(|s| format!("`{s}`")) .map(|s| format!("`{s}`"))
@ -440,7 +440,7 @@ impl FromStr for Kind {
} else { } else {
Err(ResponseError::from_msg( Err(ResponseError::from_msg(
format!( format!(
"`{}` is not a type. Available types are {}.", "`{}` is not a valid task type. Available types are {}.",
kind, kind,
enum_iterator::all::<Kind>() enum_iterator::all::<Kind>()
.map(|k| format!( .map(|k| format!(

View File

@ -55,7 +55,6 @@ rustls = "0.20.6"
rustls-pemfile = "1.0.1" rustls-pemfile = "1.0.1"
segment = { version = "0.2.1", optional = true } segment = { version = "0.2.1", optional = true }
serde = { version = "1.0.145", features = ["derive"] } serde = { version = "1.0.145", features = ["derive"] }
serde-cs = "0.2.4"
serde_json = { version = "1.0.85", features = ["preserve_order"] } serde_json = { version = "1.0.85", features = ["preserve_order"] }
sha2 = "0.10.6" sha2 = "0.10.6"
siphasher = "0.3.10" siphasher = "0.3.10"

View File

@ -4,8 +4,8 @@ use actix_web::{web, HttpRequest, HttpResponse};
use deserr::DeserializeFromValue; use deserr::DeserializeFromValue;
use meilisearch_auth::error::AuthControllerError; use meilisearch_auth::error::AuthControllerError;
use meilisearch_auth::AuthController; use meilisearch_auth::AuthController;
use meilisearch_types::error::deserr_codes::*; use meilisearch_types::error::{deserr_codes::*, DeserrQueryParamError};
use meilisearch_types::error::{Code, DeserrError, ResponseError, TakeErrorMessage}; use meilisearch_types::error::{Code, DeserrJsonError, ResponseError, TakeErrorMessage};
use meilisearch_types::keys::{Action, CreateApiKey, Key, PatchApiKey}; use meilisearch_types::keys::{Action, CreateApiKey, Key, PatchApiKey};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use time::OffsetDateTime; use time::OffsetDateTime;
@ -36,7 +36,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
pub async fn create_api_key( pub async fn create_api_key(
auth_controller: GuardedData<ActionPolicy<{ actions::KEYS_CREATE }>, AuthController>, auth_controller: GuardedData<ActionPolicy<{ actions::KEYS_CREATE }>, AuthController>,
body: ValidatedJson<CreateApiKey, DeserrError>, body: ValidatedJson<CreateApiKey, DeserrJsonError>,
_req: HttpRequest, _req: HttpRequest,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {
let v = body.into_inner(); let v = body.into_inner();
@ -51,14 +51,14 @@ pub async fn create_api_key(
} }
#[derive(DeserializeFromValue, Deserialize, Debug, Clone, Copy)] #[derive(DeserializeFromValue, Deserialize, Debug, Clone, Copy)]
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)] #[deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields)]
#[serde(rename_all = "camelCase", deny_unknown_fields)] #[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ListApiKeys { pub struct ListApiKeys {
#[serde(default)] #[serde(default)]
#[deserr(error = DeserrError<InvalidApiKeyOffset>, default, from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidApiKeyOffset>, default, from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
pub offset: usize, pub offset: usize,
#[serde(default = "PAGINATION_DEFAULT_LIMIT")] #[serde(default = "PAGINATION_DEFAULT_LIMIT")]
#[deserr(error = DeserrError<InvalidApiKeyLimit>, default = PAGINATION_DEFAULT_LIMIT(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidApiKeyLimit>, default = PAGINATION_DEFAULT_LIMIT(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
pub limit: usize, pub limit: usize,
} }
impl ListApiKeys { impl ListApiKeys {
@ -69,7 +69,7 @@ impl ListApiKeys {
pub async fn list_api_keys( pub async fn list_api_keys(
auth_controller: GuardedData<ActionPolicy<{ actions::KEYS_GET }>, AuthController>, auth_controller: GuardedData<ActionPolicy<{ actions::KEYS_GET }>, AuthController>,
list_api_keys: QueryParameter<ListApiKeys, DeserrError>, list_api_keys: QueryParameter<ListApiKeys, DeserrQueryParamError>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {
let paginate = list_api_keys.into_inner().as_pagination(); let paginate = list_api_keys.into_inner().as_pagination();
let page_view = tokio::task::spawn_blocking(move || -> Result<_, AuthControllerError> { let page_view = tokio::task::spawn_blocking(move || -> Result<_, AuthControllerError> {
@ -106,7 +106,7 @@ pub async fn get_api_key(
pub async fn patch_api_key( pub async fn patch_api_key(
auth_controller: GuardedData<ActionPolicy<{ actions::KEYS_UPDATE }>, AuthController>, auth_controller: GuardedData<ActionPolicy<{ actions::KEYS_UPDATE }>, AuthController>,
body: ValidatedJson<PatchApiKey, DeserrError>, body: ValidatedJson<PatchApiKey, DeserrJsonError>,
path: web::Path<AuthParam>, path: web::Path<AuthParam>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {
let key = path.into_inner().key; let key = path.into_inner().key;

View File

@ -10,18 +10,18 @@ use futures::StreamExt;
use index_scheduler::IndexScheduler; use index_scheduler::IndexScheduler;
use log::debug; use log::debug;
use meilisearch_types::document_formats::{read_csv, read_json, read_ndjson, PayloadType}; use meilisearch_types::document_formats::{read_csv, read_json, read_ndjson, PayloadType};
use meilisearch_types::error::deserr_codes::*; use meilisearch_types::error::{deserr_codes::*, fold_star_or, DeserrQueryParamError};
use meilisearch_types::error::{DeserrError, ResponseError, TakeErrorMessage}; use meilisearch_types::error::{DeserrJsonError, ResponseError, TakeErrorMessage};
use meilisearch_types::heed::RoTxn; use meilisearch_types::heed::RoTxn;
use meilisearch_types::index_uid::IndexUid; use meilisearch_types::index_uid::IndexUid;
use meilisearch_types::milli::update::IndexDocumentsMethod; use meilisearch_types::milli::update::IndexDocumentsMethod;
use meilisearch_types::serde_cs::vec::CS;
use meilisearch_types::star_or::StarOr; use meilisearch_types::star_or::StarOr;
use meilisearch_types::tasks::KindWithContent; use meilisearch_types::tasks::KindWithContent;
use meilisearch_types::{milli, Document, Index}; use meilisearch_types::{milli, Document, Index};
use mime::Mime; use mime::Mime;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use serde::Deserialize; use serde::Deserialize;
use serde_cs::vec::CS;
use serde_json::Value; use serde_json::Value;
use tempfile::tempfile; use tempfile::tempfile;
use tokio::fs::File; use tokio::fs::File;
@ -36,7 +36,7 @@ use crate::extractors::authentication::GuardedData;
use crate::extractors::payload::Payload; use crate::extractors::payload::Payload;
use crate::extractors::query_parameters::QueryParameter; use crate::extractors::query_parameters::QueryParameter;
use crate::extractors::sequential_extractor::SeqHandler; use crate::extractors::sequential_extractor::SeqHandler;
use crate::routes::{fold_star_or, PaginationView, SummarizedTaskView}; use crate::routes::{PaginationView, SummarizedTaskView};
static ACCEPTED_CONTENT_TYPE: Lazy<Vec<String>> = Lazy::new(|| { static ACCEPTED_CONTENT_TYPE: Lazy<Vec<String>> = Lazy::new(|| {
vec!["application/json".to_string(), "application/x-ndjson".to_string(), "text/csv".to_string()] vec!["application/json".to_string(), "application/x-ndjson".to_string(), "text/csv".to_string()]
@ -82,16 +82,17 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
} }
#[derive(Deserialize, Debug, DeserializeFromValue)] #[derive(Deserialize, Debug, DeserializeFromValue)]
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)] #[deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields)]
pub struct GetDocument { pub struct GetDocument {
#[deserr(error = DeserrError<InvalidDocumentFields>)] // TODO: strongly typed argument here
#[deserr(error = DeserrQueryParamError<InvalidDocumentFields>)]
fields: Option<CS<StarOr<String>>>, fields: Option<CS<StarOr<String>>>,
} }
pub async fn get_document( pub async fn get_document(
index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_GET }>, Data<IndexScheduler>>, index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_GET }>, Data<IndexScheduler>>,
path: web::Path<DocumentParam>, path: web::Path<DocumentParam>,
params: QueryParameter<GetDocument, DeserrError>, params: QueryParameter<GetDocument, DeserrQueryParamError>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {
let GetDocument { fields } = params.into_inner(); let GetDocument { fields } = params.into_inner();
let attributes_to_retrieve = fields.and_then(fold_star_or); let attributes_to_retrieve = fields.and_then(fold_star_or);
@ -119,20 +120,20 @@ pub async fn delete_document(
} }
#[derive(Deserialize, Debug, DeserializeFromValue)] #[derive(Deserialize, Debug, DeserializeFromValue)]
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)] #[deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields)]
pub struct BrowseQuery { pub struct BrowseQuery {
#[deserr(error = DeserrError<InvalidDocumentFields>, default, from(&String) = parse_usize_take_error_message -> TakeErrorMessage<ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidDocumentFields>, default, from(&String) = parse_usize_take_error_message -> TakeErrorMessage<ParseIntError>)]
offset: usize, offset: usize,
#[deserr(error = DeserrError<InvalidDocumentLimit>, default = crate::routes::PAGINATION_DEFAULT_LIMIT(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidDocumentLimit>, default = crate::routes::PAGINATION_DEFAULT_LIMIT(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<ParseIntError>)]
limit: usize, limit: usize,
#[deserr(error = DeserrError<InvalidDocumentLimit>)] #[deserr(error = DeserrQueryParamError<InvalidDocumentLimit>)]
fields: Option<CS<StarOr<String>>>, fields: Option<CS<StarOr<String>>>,
} }
pub async fn get_all_documents( pub async fn get_all_documents(
index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_GET }>, Data<IndexScheduler>>, index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_GET }>, Data<IndexScheduler>>,
index_uid: web::Path<String>, index_uid: web::Path<String>,
params: QueryParameter<BrowseQuery, DeserrError>, params: QueryParameter<BrowseQuery, DeserrQueryParamError>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {
debug!("called with params: {:?}", params); debug!("called with params: {:?}", params);
let BrowseQuery { limit, offset, fields } = params.into_inner(); let BrowseQuery { limit, offset, fields } = params.into_inner();
@ -148,16 +149,16 @@ pub async fn get_all_documents(
} }
#[derive(Deserialize, Debug, DeserializeFromValue)] #[derive(Deserialize, Debug, DeserializeFromValue)]
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)] #[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)]
pub struct UpdateDocumentsQuery { pub struct UpdateDocumentsQuery {
#[deserr(error = DeserrError<InvalidIndexPrimaryKey>)] #[deserr(error = DeserrJsonError<InvalidIndexPrimaryKey>)]
pub primary_key: Option<String>, pub primary_key: Option<String>,
} }
pub async fn add_documents( pub async fn add_documents(
index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_ADD }>, Data<IndexScheduler>>, index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_ADD }>, Data<IndexScheduler>>,
index_uid: web::Path<String>, index_uid: web::Path<String>,
params: QueryParameter<UpdateDocumentsQuery, DeserrError>, params: QueryParameter<UpdateDocumentsQuery, DeserrJsonError>,
body: Payload, body: Payload,
req: HttpRequest, req: HttpRequest,
analytics: web::Data<dyn Analytics>, analytics: web::Data<dyn Analytics>,
@ -185,7 +186,7 @@ pub async fn add_documents(
pub async fn update_documents( pub async fn update_documents(
index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_ADD }>, Data<IndexScheduler>>, index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_ADD }>, Data<IndexScheduler>>,
path: web::Path<String>, path: web::Path<String>,
params: QueryParameter<UpdateDocumentsQuery, DeserrError>, params: QueryParameter<UpdateDocumentsQuery, DeserrJsonError>,
body: Payload, body: Payload,
req: HttpRequest, req: HttpRequest,
analytics: web::Data<dyn Analytics>, analytics: web::Data<dyn Analytics>,

View File

@ -5,8 +5,8 @@ use actix_web::{web, HttpRequest, HttpResponse};
use deserr::{DeserializeError, DeserializeFromValue, ValuePointerRef}; use deserr::{DeserializeError, DeserializeFromValue, ValuePointerRef};
use index_scheduler::IndexScheduler; use index_scheduler::IndexScheduler;
use log::debug; use log::debug;
use meilisearch_types::error::deserr_codes::*; use meilisearch_types::error::{deserr_codes::*, unwrap_any, Code, DeserrQueryParamError};
use meilisearch_types::error::{unwrap_any, Code, DeserrError, ResponseError, TakeErrorMessage}; use meilisearch_types::error::{DeserrJsonError, ResponseError, TakeErrorMessage};
use meilisearch_types::index_uid::IndexUid; use meilisearch_types::index_uid::IndexUid;
use meilisearch_types::milli::{self, FieldDistribution, Index}; use meilisearch_types::milli::{self, FieldDistribution, Index};
use meilisearch_types::tasks::KindWithContent; use meilisearch_types::tasks::KindWithContent;
@ -72,14 +72,14 @@ impl IndexView {
} }
#[derive(DeserializeFromValue, Deserialize, Debug, Clone, Copy)] #[derive(DeserializeFromValue, Deserialize, Debug, Clone, Copy)]
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)] #[deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields)]
#[serde(rename_all = "camelCase", deny_unknown_fields)] #[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ListIndexes { pub struct ListIndexes {
#[serde(default)] #[serde(default)]
#[deserr(error = DeserrError<InvalidIndexOffset>, default, from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidIndexOffset>, default, from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
pub offset: usize, pub offset: usize,
#[serde(default = "PAGINATION_DEFAULT_LIMIT")] #[serde(default = "PAGINATION_DEFAULT_LIMIT")]
#[deserr(error = DeserrError<InvalidIndexLimit>, default = PAGINATION_DEFAULT_LIMIT(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidIndexLimit>, default = PAGINATION_DEFAULT_LIMIT(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
pub limit: usize, pub limit: usize,
} }
impl ListIndexes { impl ListIndexes {
@ -90,7 +90,7 @@ impl ListIndexes {
pub async fn list_indexes( pub async fn list_indexes(
index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_GET }>, Data<IndexScheduler>>, index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_GET }>, Data<IndexScheduler>>,
paginate: QueryParameter<ListIndexes, DeserrError>, paginate: QueryParameter<ListIndexes, DeserrQueryParamError>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {
let search_rules = &index_scheduler.filters().search_rules; let search_rules = &index_scheduler.filters().search_rules;
let indexes: Vec<_> = index_scheduler.indexes()?; let indexes: Vec<_> = index_scheduler.indexes()?;
@ -107,17 +107,17 @@ pub async fn list_indexes(
} }
#[derive(DeserializeFromValue, Debug)] #[derive(DeserializeFromValue, Debug)]
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)] #[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)]
pub struct IndexCreateRequest { pub struct IndexCreateRequest {
#[deserr(error = DeserrError<InvalidIndexUid>, missing_field_error = DeserrError::missing_index_uid)] #[deserr(error = DeserrJsonError<InvalidIndexUid>, missing_field_error = DeserrJsonError::missing_index_uid)]
uid: String, uid: String,
#[deserr(error = DeserrError<InvalidIndexPrimaryKey>)] #[deserr(error = DeserrJsonError<InvalidIndexPrimaryKey>)]
primary_key: Option<String>, primary_key: Option<String>,
} }
pub async fn create_index( pub async fn create_index(
index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_CREATE }>, Data<IndexScheduler>>, index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_CREATE }>, Data<IndexScheduler>>,
body: ValidatedJson<IndexCreateRequest, DeserrError>, body: ValidatedJson<IndexCreateRequest, DeserrJsonError>,
req: HttpRequest, req: HttpRequest,
analytics: web::Data<dyn Analytics>, analytics: web::Data<dyn Analytics>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {
@ -146,8 +146,8 @@ fn deny_immutable_fields_index(
field: &str, field: &str,
accepted: &[&str], accepted: &[&str],
location: ValuePointerRef, location: ValuePointerRef,
) -> DeserrError { ) -> DeserrJsonError {
let mut error = unwrap_any(DeserrError::<BadRequest>::error::<Infallible>( let mut error = unwrap_any(DeserrJsonError::<BadRequest>::error::<Infallible>(
None, None,
deserr::ErrorKind::UnknownKey { key: field, accepted }, deserr::ErrorKind::UnknownKey { key: field, accepted },
location, location,
@ -162,9 +162,9 @@ fn deny_immutable_fields_index(
error error
} }
#[derive(DeserializeFromValue, Debug)] #[derive(DeserializeFromValue, Debug)]
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields = deny_immutable_fields_index)] #[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields = deny_immutable_fields_index)]
pub struct UpdateIndexRequest { pub struct UpdateIndexRequest {
#[deserr(error = DeserrError<InvalidIndexPrimaryKey>)] #[deserr(error = DeserrJsonError<InvalidIndexPrimaryKey>)]
primary_key: Option<String>, primary_key: Option<String>,
} }
@ -183,7 +183,7 @@ pub async fn get_index(
pub async fn update_index( pub async fn update_index(
index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_UPDATE }>, Data<IndexScheduler>>, index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_UPDATE }>, Data<IndexScheduler>>,
path: web::Path<String>, path: web::Path<String>,
body: ValidatedJson<UpdateIndexRequest, DeserrError>, body: ValidatedJson<UpdateIndexRequest, DeserrJsonError>,
req: HttpRequest, req: HttpRequest,
analytics: web::Data<dyn Analytics>, analytics: web::Data<dyn Analytics>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {

View File

@ -5,9 +5,12 @@ use actix_web::{web, HttpRequest, HttpResponse};
use index_scheduler::IndexScheduler; use index_scheduler::IndexScheduler;
use log::debug; use log::debug;
use meilisearch_auth::IndexSearchRules; use meilisearch_auth::IndexSearchRules;
use meilisearch_types::error::deserr_codes::*; use meilisearch_types::error::{
use meilisearch_types::error::{DeserrError, ResponseError, TakeErrorMessage}; deserr_codes::*, parse_option_usize_query_param, parse_usize_query_param,
use serde_cs::vec::CS; DeserrQueryParamError, DetailedParseIntError,
};
use meilisearch_types::error::{DeserrJsonError, ResponseError, TakeErrorMessage};
use meilisearch_types::serde_cs::vec::CS;
use serde_json::Value; use serde_json::Value;
use crate::analytics::{Analytics, SearchAggregator}; use crate::analytics::{Analytics, SearchAggregator};
@ -16,7 +19,6 @@ use crate::extractors::authentication::GuardedData;
use crate::extractors::json::ValidatedJson; use crate::extractors::json::ValidatedJson;
use crate::extractors::query_parameters::QueryParameter; use crate::extractors::query_parameters::QueryParameter;
use crate::extractors::sequential_extractor::SeqHandler; use crate::extractors::sequential_extractor::SeqHandler;
use crate::routes::from_string_to_option_take_error_message;
use crate::search::{ use crate::search::{
perform_search, MatchingStrategy, SearchQuery, DEFAULT_CROP_LENGTH, DEFAULT_CROP_MARKER, perform_search, MatchingStrategy, SearchQuery, DEFAULT_CROP_LENGTH, DEFAULT_CROP_MARKER,
DEFAULT_HIGHLIGHT_POST_TAG, DEFAULT_HIGHLIGHT_PRE_TAG, DEFAULT_SEARCH_LIMIT, DEFAULT_HIGHLIGHT_POST_TAG, DEFAULT_HIGHLIGHT_PRE_TAG, DEFAULT_SEARCH_LIMIT,
@ -44,41 +46,41 @@ pub fn parse_bool_take_error_message(
} }
#[derive(Debug, deserr::DeserializeFromValue)] #[derive(Debug, deserr::DeserializeFromValue)]
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)] #[deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields)]
pub struct SearchQueryGet { pub struct SearchQueryGet {
#[deserr(error = DeserrError<InvalidSearchQ>)] #[deserr(error = DeserrQueryParamError<InvalidSearchQ>)]
q: Option<String>, q: Option<String>,
#[deserr(error = DeserrError<InvalidSearchOffset>, default = DEFAULT_SEARCH_OFFSET(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidSearchOffset>, default = DEFAULT_SEARCH_OFFSET(), from(String) = parse_usize_query_param -> TakeErrorMessage<DetailedParseIntError>)]
offset: usize, offset: usize,
#[deserr(error = DeserrError<InvalidSearchLimit>, default = DEFAULT_SEARCH_LIMIT(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidSearchLimit>, default = DEFAULT_SEARCH_LIMIT(), from(String) = parse_usize_query_param -> TakeErrorMessage<DetailedParseIntError>)]
limit: usize, limit: usize,
#[deserr(error = DeserrError<InvalidSearchPage>, from(&String) = from_string_to_option_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidSearchPage>, from(Option<String>) = parse_option_usize_query_param -> TakeErrorMessage<std::num::ParseIntError>)]
page: Option<usize>, page: Option<usize>,
#[deserr(error = DeserrError<InvalidSearchHitsPerPage>, from(&String) = from_string_to_option_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidSearchHitsPerPage>, from(Option<String>) = parse_option_usize_query_param -> TakeErrorMessage<std::num::ParseIntError>)]
hits_per_page: Option<usize>, hits_per_page: Option<usize>,
#[deserr(error = DeserrError<InvalidSearchAttributesToRetrieve>)] #[deserr(error = DeserrQueryParamError<InvalidSearchAttributesToRetrieve>)]
attributes_to_retrieve: Option<CS<String>>, attributes_to_retrieve: Option<CS<String>>,
#[deserr(error = DeserrError<InvalidSearchAttributesToCrop>)] #[deserr(error = DeserrQueryParamError<InvalidSearchAttributesToCrop>)]
attributes_to_crop: Option<CS<String>>, attributes_to_crop: Option<CS<String>>,
#[deserr(error = DeserrError<InvalidSearchCropLength>, default = DEFAULT_CROP_LENGTH(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidSearchCropLength>, default = DEFAULT_CROP_LENGTH(), from(String) = parse_usize_query_param -> TakeErrorMessage<DetailedParseIntError>)]
crop_length: usize, crop_length: usize,
#[deserr(error = DeserrError<InvalidSearchAttributesToHighlight>)] #[deserr(error = DeserrQueryParamError<InvalidSearchAttributesToHighlight>)]
attributes_to_highlight: Option<CS<String>>, attributes_to_highlight: Option<CS<String>>,
#[deserr(error = DeserrError<InvalidSearchFilter>)] #[deserr(error = DeserrQueryParamError<InvalidSearchFilter>)]
filter: Option<String>, filter: Option<String>,
#[deserr(error = DeserrError<InvalidSearchSort>)] #[deserr(error = DeserrQueryParamError<InvalidSearchSort>)]
sort: Option<String>, sort: Option<String>,
#[deserr(error = DeserrError<InvalidSearchShowMatchesPosition>, default, from(&String) = parse_bool_take_error_message -> TakeErrorMessage<std::str::ParseBoolError>)] #[deserr(error = DeserrQueryParamError<InvalidSearchShowMatchesPosition>, default, from(&String) = parse_bool_take_error_message -> TakeErrorMessage<std::str::ParseBoolError>)]
show_matches_position: bool, show_matches_position: bool,
#[deserr(error = DeserrError<InvalidSearchFacets>)] #[deserr(error = DeserrQueryParamError<InvalidSearchFacets>)]
facets: Option<CS<String>>, facets: Option<CS<String>>,
#[deserr(error = DeserrError<InvalidSearchHighlightPreTag>, default = DEFAULT_HIGHLIGHT_PRE_TAG())] #[deserr(error = DeserrQueryParamError<InvalidSearchHighlightPreTag>, default = DEFAULT_HIGHLIGHT_PRE_TAG())]
highlight_pre_tag: String, highlight_pre_tag: String,
#[deserr(error = DeserrError<InvalidSearchHighlightPostTag>, default = DEFAULT_HIGHLIGHT_POST_TAG())] #[deserr(error = DeserrQueryParamError<InvalidSearchHighlightPostTag>, default = DEFAULT_HIGHLIGHT_POST_TAG())]
highlight_post_tag: String, highlight_post_tag: String,
#[deserr(error = DeserrError<InvalidSearchCropMarker>, default = DEFAULT_CROP_MARKER())] #[deserr(error = DeserrQueryParamError<InvalidSearchCropMarker>, default = DEFAULT_CROP_MARKER())]
crop_marker: String, crop_marker: String,
#[deserr(error = DeserrError<InvalidSearchMatchingStrategy>, default)] #[deserr(error = DeserrQueryParamError<InvalidSearchMatchingStrategy>, default)]
matching_strategy: MatchingStrategy, matching_strategy: MatchingStrategy,
} }
@ -162,7 +164,7 @@ fn fix_sort_query_parameters(sort_query: &str) -> Vec<String> {
pub async fn search_with_url_query( pub async fn search_with_url_query(
index_scheduler: GuardedData<ActionPolicy<{ actions::SEARCH }>, Data<IndexScheduler>>, index_scheduler: GuardedData<ActionPolicy<{ actions::SEARCH }>, Data<IndexScheduler>>,
index_uid: web::Path<String>, index_uid: web::Path<String>,
params: QueryParameter<SearchQueryGet, DeserrError>, params: QueryParameter<SearchQueryGet, DeserrQueryParamError>,
req: HttpRequest, req: HttpRequest,
analytics: web::Data<dyn Analytics>, analytics: web::Data<dyn Analytics>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {
@ -194,7 +196,7 @@ pub async fn search_with_url_query(
pub async fn search_with_post( pub async fn search_with_post(
index_scheduler: GuardedData<ActionPolicy<{ actions::SEARCH }>, Data<IndexScheduler>>, index_scheduler: GuardedData<ActionPolicy<{ actions::SEARCH }>, Data<IndexScheduler>>,
index_uid: web::Path<String>, index_uid: web::Path<String>,
params: ValidatedJson<SearchQuery, DeserrError>, params: ValidatedJson<SearchQuery, DeserrJsonError>,
req: HttpRequest, req: HttpRequest,
analytics: web::Data<dyn Analytics>, analytics: web::Data<dyn Analytics>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {

View File

@ -2,7 +2,7 @@ use actix_web::web::Data;
use actix_web::{web, HttpRequest, HttpResponse}; use actix_web::{web, HttpRequest, HttpResponse};
use index_scheduler::IndexScheduler; use index_scheduler::IndexScheduler;
use log::debug; use log::debug;
use meilisearch_types::error::{DeserrError, ResponseError}; use meilisearch_types::error::{DeserrJsonError, ResponseError};
use meilisearch_types::index_uid::IndexUid; use meilisearch_types::index_uid::IndexUid;
use meilisearch_types::settings::{settings, RankingRuleView, Settings, Unchecked}; use meilisearch_types::settings::{settings, RankingRuleView, Settings, Unchecked};
use meilisearch_types::tasks::KindWithContent; use meilisearch_types::tasks::KindWithContent;
@ -130,7 +130,7 @@ make_setting_route!(
"/filterable-attributes", "/filterable-attributes",
put, put,
std::collections::BTreeSet<String>, std::collections::BTreeSet<String>,
meilisearch_types::error::DeserrError< meilisearch_types::error::DeserrJsonError<
meilisearch_types::error::deserr_codes::InvalidSettingsFilterableAttributes, meilisearch_types::error::deserr_codes::InvalidSettingsFilterableAttributes,
>, >,
filterable_attributes, filterable_attributes,
@ -156,7 +156,7 @@ make_setting_route!(
"/sortable-attributes", "/sortable-attributes",
put, put,
std::collections::BTreeSet<String>, std::collections::BTreeSet<String>,
meilisearch_types::error::DeserrError< meilisearch_types::error::DeserrJsonError<
meilisearch_types::error::deserr_codes::InvalidSettingsSortableAttributes, meilisearch_types::error::deserr_codes::InvalidSettingsSortableAttributes,
>, >,
sortable_attributes, sortable_attributes,
@ -182,7 +182,7 @@ make_setting_route!(
"/displayed-attributes", "/displayed-attributes",
put, put,
Vec<String>, Vec<String>,
meilisearch_types::error::DeserrError< meilisearch_types::error::DeserrJsonError<
meilisearch_types::error::deserr_codes::InvalidSettingsDisplayedAttributes, meilisearch_types::error::deserr_codes::InvalidSettingsDisplayedAttributes,
>, >,
displayed_attributes, displayed_attributes,
@ -208,7 +208,7 @@ make_setting_route!(
"/typo-tolerance", "/typo-tolerance",
patch, patch,
meilisearch_types::settings::TypoSettings, meilisearch_types::settings::TypoSettings,
meilisearch_types::error::DeserrError< meilisearch_types::error::DeserrJsonError<
meilisearch_types::error::deserr_codes::InvalidSettingsTypoTolerance, meilisearch_types::error::deserr_codes::InvalidSettingsTypoTolerance,
>, >,
typo_tolerance, typo_tolerance,
@ -253,7 +253,7 @@ make_setting_route!(
"/searchable-attributes", "/searchable-attributes",
put, put,
Vec<String>, Vec<String>,
meilisearch_types::error::DeserrError< meilisearch_types::error::DeserrJsonError<
meilisearch_types::error::deserr_codes::InvalidSettingsSearchableAttributes, meilisearch_types::error::deserr_codes::InvalidSettingsSearchableAttributes,
>, >,
searchable_attributes, searchable_attributes,
@ -279,7 +279,7 @@ make_setting_route!(
"/stop-words", "/stop-words",
put, put,
std::collections::BTreeSet<String>, std::collections::BTreeSet<String>,
meilisearch_types::error::DeserrError< meilisearch_types::error::DeserrJsonError<
meilisearch_types::error::deserr_codes::InvalidSettingsStopWords, meilisearch_types::error::deserr_codes::InvalidSettingsStopWords,
>, >,
stop_words, stop_words,
@ -304,7 +304,7 @@ make_setting_route!(
"/synonyms", "/synonyms",
put, put,
std::collections::BTreeMap<String, Vec<String>>, std::collections::BTreeMap<String, Vec<String>>,
meilisearch_types::error::DeserrError< meilisearch_types::error::DeserrJsonError<
meilisearch_types::error::deserr_codes::InvalidSettingsSynonyms, meilisearch_types::error::deserr_codes::InvalidSettingsSynonyms,
>, >,
synonyms, synonyms,
@ -329,7 +329,7 @@ make_setting_route!(
"/distinct-attribute", "/distinct-attribute",
put, put,
String, String,
meilisearch_types::error::DeserrError< meilisearch_types::error::DeserrJsonError<
meilisearch_types::error::deserr_codes::InvalidSettingsDistinctAttribute, meilisearch_types::error::deserr_codes::InvalidSettingsDistinctAttribute,
>, >,
distinct_attribute, distinct_attribute,
@ -353,7 +353,7 @@ make_setting_route!(
"/ranking-rules", "/ranking-rules",
put, put,
Vec<meilisearch_types::settings::RankingRuleView>, Vec<meilisearch_types::settings::RankingRuleView>,
meilisearch_types::error::DeserrError< meilisearch_types::error::DeserrJsonError<
meilisearch_types::error::deserr_codes::InvalidSettingsRankingRules, meilisearch_types::error::deserr_codes::InvalidSettingsRankingRules,
>, >,
ranking_rules, ranking_rules,
@ -384,7 +384,7 @@ make_setting_route!(
"/faceting", "/faceting",
patch, patch,
meilisearch_types::settings::FacetingSettings, meilisearch_types::settings::FacetingSettings,
meilisearch_types::error::DeserrError< meilisearch_types::error::DeserrJsonError<
meilisearch_types::error::deserr_codes::InvalidSettingsFaceting, meilisearch_types::error::deserr_codes::InvalidSettingsFaceting,
>, >,
faceting, faceting,
@ -409,7 +409,7 @@ make_setting_route!(
"/pagination", "/pagination",
patch, patch,
meilisearch_types::settings::PaginationSettings, meilisearch_types::settings::PaginationSettings,
meilisearch_types::error::DeserrError< meilisearch_types::error::DeserrJsonError<
meilisearch_types::error::deserr_codes::InvalidSettingsPagination, meilisearch_types::error::deserr_codes::InvalidSettingsPagination,
>, >,
pagination, pagination,
@ -461,7 +461,7 @@ generate_configure!(
pub async fn update_all( pub async fn update_all(
index_scheduler: GuardedData<ActionPolicy<{ actions::SETTINGS_UPDATE }>, Data<IndexScheduler>>, index_scheduler: GuardedData<ActionPolicy<{ actions::SETTINGS_UPDATE }>, Data<IndexScheduler>>,
index_uid: web::Path<String>, index_uid: web::Path<String>,
body: ValidatedJson<Settings<Unchecked>, DeserrError>, body: ValidatedJson<Settings<Unchecked>, DeserrJsonError>,
req: HttpRequest, req: HttpRequest,
analytics: web::Data<dyn Analytics>, analytics: web::Data<dyn Analytics>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {

View File

@ -5,9 +5,8 @@ use actix_web::web::Data;
use actix_web::{web, HttpRequest, HttpResponse}; use actix_web::{web, HttpRequest, HttpResponse};
use index_scheduler::{IndexScheduler, Query}; use index_scheduler::{IndexScheduler, Query};
use log::debug; use log::debug;
use meilisearch_types::error::{ResponseError, TakeErrorMessage}; use meilisearch_types::error::ResponseError;
use meilisearch_types::settings::{Settings, Unchecked}; use meilisearch_types::settings::{Settings, Unchecked};
use meilisearch_types::star_or::StarOr;
use meilisearch_types::tasks::{Kind, Status, Task, TaskId}; use meilisearch_types::tasks::{Kind, Status, Task, TaskId};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::json; use serde_json::json;
@ -35,35 +34,12 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
.service(web::scope("/swap-indexes").configure(swap_indexes::configure)); .service(web::scope("/swap-indexes").configure(swap_indexes::configure));
} }
/// Extracts the raw values from the `StarOr` types and
/// return None if a `StarOr::Star` is encountered.
pub fn fold_star_or<T, O>(content: impl IntoIterator<Item = StarOr<T>>) -> Option<O>
where
O: FromIterator<T>,
{
content
.into_iter()
.map(|value| match value {
StarOr::Star => None,
StarOr::Other(val) => Some(val),
})
.collect()
}
pub fn from_string_to_option<T, E>(input: &str) -> Result<Option<T>, E> pub fn from_string_to_option<T, E>(input: &str) -> Result<Option<T>, E>
where where
T: FromStr<Err = E>, T: FromStr<Err = E>,
{ {
Ok(Some(input.parse()?)) Ok(Some(input.parse()?))
} }
pub fn from_string_to_option_take_error_message<T, E>(
input: &str,
) -> Result<Option<T>, TakeErrorMessage<E>>
where
T: FromStr<Err = E>,
{
Ok(Some(input.parse().map_err(TakeErrorMessage)?))
}
const PAGINATION_DEFAULT_LIMIT: fn() -> usize = || 20; const PAGINATION_DEFAULT_LIMIT: fn() -> usize = || 20;

View File

@ -3,7 +3,7 @@ use actix_web::{web, HttpRequest, HttpResponse};
use deserr::DeserializeFromValue; use deserr::DeserializeFromValue;
use index_scheduler::IndexScheduler; use index_scheduler::IndexScheduler;
use meilisearch_types::error::deserr_codes::InvalidSwapIndexes; use meilisearch_types::error::deserr_codes::InvalidSwapIndexes;
use meilisearch_types::error::{DeserrError, ResponseError}; use meilisearch_types::error::{DeserrJsonError, ResponseError};
use meilisearch_types::tasks::{IndexSwap, KindWithContent}; use meilisearch_types::tasks::{IndexSwap, KindWithContent};
use serde_json::json; use serde_json::json;
@ -20,15 +20,15 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
} }
#[derive(DeserializeFromValue, Debug, Clone, PartialEq, Eq)] #[derive(DeserializeFromValue, Debug, Clone, PartialEq, Eq)]
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)] #[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)]
pub struct SwapIndexesPayload { pub struct SwapIndexesPayload {
#[deserr(error = DeserrError<InvalidSwapIndexes>)] #[deserr(error = DeserrJsonError<InvalidSwapIndexes>)]
indexes: Vec<String>, indexes: Vec<String>,
} }
pub async fn swap_indexes( pub async fn swap_indexes(
index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_SWAP }>, Data<IndexScheduler>>, index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_SWAP }>, Data<IndexScheduler>>,
params: ValidatedJson<Vec<SwapIndexesPayload>, DeserrError>, params: ValidatedJson<Vec<SwapIndexesPayload>, DeserrJsonError>,
req: HttpRequest, req: HttpRequest,
analytics: web::Data<dyn Analytics>, analytics: web::Data<dyn Analytics>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {
@ -44,6 +44,7 @@ pub async fn swap_indexes(
let mut swaps = vec![]; let mut swaps = vec![];
for SwapIndexesPayload { indexes } in params.into_iter() { for SwapIndexesPayload { indexes } in params.into_iter() {
// TODO: switch to deserr
let (lhs, rhs) = match indexes.as_slice() { let (lhs, rhs) = match indexes.as_slice() {
[lhs, rhs] => (lhs, rhs), [lhs, rhs] => (lhs, rhs),
_ => { _ => {

View File

@ -1,13 +1,15 @@
use std::num::ParseIntError;
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 deserr::DeserializeFromValue; use deserr::DeserializeFromValue;
use index_scheduler::{IndexScheduler, Query, TaskId}; use index_scheduler::{IndexScheduler, Query, TaskId};
use meilisearch_types::error::deserr_codes::*; use meilisearch_types::error::{
use meilisearch_types::error::{DeserrError, ResponseError, TakeErrorMessage}; deserr_codes::*, parse_option_cs_star_or, parse_option_u32_query_param,
parse_option_vec_u32_query_param, DeserrQueryParamError, DetailedParseIntError,
TakeErrorMessage,
};
use meilisearch_types::error::{parse_u32_query_param, ResponseError};
use meilisearch_types::index_uid::IndexUid; use meilisearch_types::index_uid::IndexUid;
use meilisearch_types::serde_cs;
use meilisearch_types::settings::{Settings, Unchecked}; use meilisearch_types::settings::{Settings, Unchecked};
use meilisearch_types::star_or::StarOr; use meilisearch_types::star_or::StarOr;
use meilisearch_types::tasks::{ use meilisearch_types::tasks::{
@ -21,7 +23,7 @@ use time::macros::format_description;
use time::{Date, Duration, OffsetDateTime, Time}; use time::{Date, Duration, OffsetDateTime, Time};
use tokio::task; use tokio::task;
use super::{fold_star_or, SummarizedTaskView}; use super::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;
@ -164,108 +166,70 @@ impl From<Details> for DetailsView {
} }
} }
fn parse_option_cs<T: FromStr>(
s: Option<CS<String>>,
) -> Result<Option<Vec<T>>, TakeErrorMessage<T::Err>> {
if let Some(s) = s {
s.into_iter()
.map(|s| T::from_str(&s))
.collect::<Result<Vec<T>, T::Err>>()
.map_err(TakeErrorMessage)
.map(Some)
} else {
Ok(None)
}
}
fn parse_option_cs_star_or<T: FromStr>(
s: Option<CS<StarOr<String>>>,
) -> 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 {
Ok(None)
}
}
fn parse_option_str<T: FromStr>(s: Option<String>) -> Result<Option<T>, TakeErrorMessage<T::Err>> {
if let Some(s) = s {
T::from_str(&s).map_err(TakeErrorMessage).map(Some)
} else {
Ok(None)
}
}
fn parse_str<T: FromStr>(s: String) -> Result<T, TakeErrorMessage<T::Err>> {
T::from_str(&s).map_err(TakeErrorMessage)
}
#[derive(Debug, DeserializeFromValue)] #[derive(Debug, DeserializeFromValue)]
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)] #[deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields)]
pub struct TasksFilterQuery { pub struct TasksFilterQuery {
#[deserr(error = DeserrError<InvalidTaskLimit>, default = DEFAULT_LIMIT(), from(String) = parse_str::<u32> -> TakeErrorMessage<ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskLimit>, default = DEFAULT_LIMIT(), from(String) = parse_u32_query_param -> TakeErrorMessage<DetailedParseIntError>)]
pub limit: u32, pub limit: u32,
#[deserr(error = DeserrError<InvalidTaskFrom>, from(Option<String>) = parse_option_str::<TaskId> -> TakeErrorMessage<ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskFrom>, from(Option<String>) = parse_option_u32_query_param -> TakeErrorMessage<DetailedParseIntError>)]
pub from: Option<TaskId>, pub from: Option<TaskId>,
#[deserr(error = DeserrError<InvalidTaskUids>, from(Option<CS<String>>) = parse_option_cs::<u32> -> TakeErrorMessage<ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskUids>, from(Option<CS<String>>) = parse_option_vec_u32_query_param -> TakeErrorMessage<DetailedParseIntError>)]
pub uids: Option<Vec<u32>>, pub uids: Option<Vec<u32>>,
#[deserr(error = DeserrError<InvalidTaskCanceledBy>, from(Option<CS<String>>) = parse_option_cs::<u32> -> TakeErrorMessage<ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskCanceledBy>, from(Option<CS<String>>) = parse_option_vec_u32_query_param -> TakeErrorMessage<DetailedParseIntError>)]
pub canceled_by: Option<Vec<u32>>, 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>)] #[deserr(error = DeserrQueryParamError<InvalidTaskTypes>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Kind> -> TakeErrorMessage<ResponseError>)]
pub types: Option<Vec<Kind>>, pub types: Option<Vec<Kind>>,
#[deserr(error = DeserrError<InvalidTaskStatuses>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Status> -> TakeErrorMessage<ResponseError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskStatuses>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Status> -> TakeErrorMessage<ResponseError>)]
pub statuses: Option<Vec<Status>>, pub statuses: Option<Vec<Status>>,
#[deserr(error = DeserrError<InvalidIndexUid>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<IndexUid> -> TakeErrorMessage<ResponseError>)] #[deserr(error = DeserrQueryParamError<InvalidIndexUid>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<IndexUid> -> TakeErrorMessage<ResponseError>)]
pub index_uids: Option<Vec<IndexUid>>, pub index_uids: Option<Vec<IndexUid>>,
#[deserr(error = DeserrError<InvalidTaskAfterEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskAfterEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
pub after_enqueued_at: Option<OffsetDateTime>, pub after_enqueued_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskBeforeEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskBeforeEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
pub before_enqueued_at: Option<OffsetDateTime>, pub before_enqueued_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskAfterStartedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskAfterStartedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
pub after_started_at: Option<OffsetDateTime>, pub after_started_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskBeforeStartedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskBeforeStartedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
pub before_started_at: Option<OffsetDateTime>, pub before_started_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskAfterFinishedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskAfterFinishedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
pub after_finished_at: Option<OffsetDateTime>, pub after_finished_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskBeforeFinishedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskBeforeFinishedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
pub before_finished_at: Option<OffsetDateTime>, pub before_finished_at: Option<OffsetDateTime>,
} }
#[derive(Deserialize, Debug, DeserializeFromValue)] #[derive(Deserialize, Debug, DeserializeFromValue)]
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)] #[deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields)]
pub struct TaskDeletionOrCancelationQuery { pub struct TaskDeletionOrCancelationQuery {
#[deserr(error = DeserrError<InvalidTaskUids>, from(Option<CS<String>>) = parse_option_cs::<u32> -> TakeErrorMessage<ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskUids>, from(Option<CS<String>>) = parse_option_vec_u32_query_param -> TakeErrorMessage<DetailedParseIntError>)]
pub uids: Option<Vec<u32>>, pub uids: Option<Vec<u32>>,
#[deserr(error = DeserrError<InvalidTaskCanceledBy>, from(Option<CS<String>>) = parse_option_cs::<u32> -> TakeErrorMessage<ParseIntError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskCanceledBy>, from(Option<CS<String>>) = parse_option_vec_u32_query_param -> TakeErrorMessage<DetailedParseIntError>)]
pub canceled_by: Option<Vec<u32>>, 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>)] #[deserr(error = DeserrQueryParamError<InvalidTaskTypes>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Kind> -> TakeErrorMessage<ResponseError>)]
pub types: Option<Vec<Kind>>, pub types: Option<Vec<Kind>>,
#[deserr(error = DeserrError<InvalidTaskStatuses>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Status> -> TakeErrorMessage<ResponseError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskStatuses>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Status> -> TakeErrorMessage<ResponseError>)]
pub statuses: Option<Vec<Status>>, pub statuses: Option<Vec<Status>>,
#[deserr(error = DeserrError<InvalidIndexUid>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<IndexUid> -> TakeErrorMessage<ResponseError>)] #[deserr(error = DeserrQueryParamError<InvalidIndexUid>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<IndexUid> -> TakeErrorMessage<ResponseError>)]
pub index_uids: Option<Vec<IndexUid>>, pub index_uids: Option<Vec<IndexUid>>,
#[deserr(error = DeserrError<InvalidTaskAfterEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskAfterEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
pub after_enqueued_at: Option<OffsetDateTime>, pub after_enqueued_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskBeforeEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskBeforeEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
pub before_enqueued_at: Option<OffsetDateTime>, pub before_enqueued_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskAfterStartedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskAfterStartedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
pub after_started_at: Option<OffsetDateTime>, pub after_started_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskBeforeStartedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskBeforeStartedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
pub before_started_at: Option<OffsetDateTime>, pub before_started_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskAfterFinishedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskAfterFinishedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
pub after_finished_at: Option<OffsetDateTime>, pub after_finished_at: Option<OffsetDateTime>,
#[deserr(error = DeserrError<InvalidTaskBeforeFinishedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)] #[deserr(error = DeserrQueryParamError<InvalidTaskBeforeFinishedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
pub before_finished_at: Option<OffsetDateTime>, 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: QueryParameter<TaskDeletionOrCancelationQuery, DeserrError>, params: QueryParameter<TaskDeletionOrCancelationQuery, DeserrQueryParamError>,
req: HttpRequest, req: HttpRequest,
analytics: web::Data<dyn Analytics>, analytics: web::Data<dyn Analytics>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {
@ -337,7 +301,7 @@ 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: QueryParameter<TaskDeletionOrCancelationQuery, DeserrError>, params: QueryParameter<TaskDeletionOrCancelationQuery, DeserrQueryParamError>,
req: HttpRequest, req: HttpRequest,
analytics: web::Data<dyn Analytics>, analytics: web::Data<dyn Analytics>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {
@ -418,7 +382,7 @@ 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: QueryParameter<TasksFilterQuery, DeserrError>, params: QueryParameter<TasksFilterQuery, DeserrQueryParamError>,
req: HttpRequest, req: HttpRequest,
analytics: web::Data<dyn Analytics>, analytics: web::Data<dyn Analytics>,
) -> Result<HttpResponse, ResponseError> { ) -> Result<HttpResponse, ResponseError> {
@ -584,16 +548,16 @@ impl std::error::Error for InvalidTaskDateError {}
mod tests { mod tests {
use deserr::DeserializeFromValue; use deserr::DeserializeFromValue;
use meili_snap::snapshot; use meili_snap::snapshot;
use meilisearch_types::error::DeserrError; use meilisearch_types::error::DeserrQueryParamError;
use crate::extractors::query_parameters::QueryParameter; use crate::extractors::query_parameters::QueryParameter;
use crate::routes::tasks::{TaskDeletionOrCancelationQuery, TasksFilterQuery}; use crate::routes::tasks::{TaskDeletionOrCancelationQuery, TasksFilterQuery};
fn deserr_query_params<T>(j: &str) -> Result<T, actix_web::Error> fn deserr_query_params<T>(j: &str) -> Result<T, actix_web::Error>
where where
T: DeserializeFromValue<DeserrError>, T: DeserializeFromValue<DeserrQueryParamError>,
{ {
QueryParameter::<T, DeserrError>::from_query(j).map(|p| p.0) QueryParameter::<T, DeserrQueryParamError>::from_query(j).map(|p| p.0)
} }
#[test] #[test]
@ -634,33 +598,33 @@ mod tests {
{ {
let params = "afterFinishedAt=2021"; let params = "afterFinishedAt=2021";
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err(); let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
snapshot!(format!("{err}"), @"`2021` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterFinishedAt`."); snapshot!(format!("{err}"), @"Invalid value in parameter `afterFinishedAt`: `2021` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.");
} }
{ {
let params = "beforeFinishedAt=2021"; let params = "beforeFinishedAt=2021";
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err(); let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
snapshot!(format!("{err}"), @"`2021` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeFinishedAt`."); snapshot!(format!("{err}"), @"Invalid value in parameter `beforeFinishedAt`: `2021` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.");
} }
{ {
let params = "afterEnqueuedAt=2021-12"; let params = "afterEnqueuedAt=2021-12";
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err(); let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
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`."); snapshot!(format!("{err}"), @"Invalid value in parameter `afterEnqueuedAt`: `2021-12` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.");
} }
{ {
let params = "beforeEnqueuedAt=2021-12-03T23"; let params = "beforeEnqueuedAt=2021-12-03T23";
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err(); let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
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`."); snapshot!(format!("{err}"), @"Invalid value in parameter `beforeEnqueuedAt`: `2021-12-03T23` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.");
} }
{ {
let params = "afterStartedAt=2021-12-03T23:45"; let params = "afterStartedAt=2021-12-03T23:45";
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err(); let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
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`."); snapshot!(format!("{err}"), @"Invalid value in parameter `afterStartedAt`: `2021-12-03T23:45` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.");
} }
{ {
let params = "beforeStartedAt=2021-12-03T23:45"; let params = "beforeStartedAt=2021-12-03T23:45";
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err(); let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
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`."); snapshot!(format!("{err}"), @"Invalid value in parameter `beforeStartedAt`: `2021-12-03T23:45` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.");
} }
} }
@ -679,12 +643,12 @@ mod tests {
{ {
let params = "uids=78,hello,world"; let params = "uids=78,hello,world";
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err(); let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
snapshot!(format!("{err}"), @"invalid digit found in string at `.uids`."); snapshot!(format!("{err}"), @"Invalid value in parameter `uids`: could not parse `hello` as a positive integer");
} }
{ {
let params = "uids=cat"; let params = "uids=cat";
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err(); let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
snapshot!(format!("{err}"), @"invalid digit found in string at `.uids`."); snapshot!(format!("{err}"), @"Invalid value in parameter `uids`: could not parse `cat` as a positive integer");
} }
} }
@ -703,7 +667,7 @@ mod tests {
{ {
let params = "statuses=finished"; let params = "statuses=finished";
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err(); let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
snapshot!(format!("{err}"), @"`finished` is not a status. Available status are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. at `.statuses`."); snapshot!(format!("{err}"), @"Invalid value in parameter `statuses`: `finished` is not a valid task status. Available statuses are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`.");
} }
} }
#[test] #[test]
@ -721,7 +685,7 @@ mod tests {
{ {
let params = "types=createIndex"; let params = "types=createIndex";
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err(); let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
snapshot!(format!("{err}"), @"`createIndex` is not a type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`. at `.types`."); snapshot!(format!("{err}"), @"Invalid value in parameter `types`: `createIndex` is not a valid task type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`.");
} }
} }
#[test] #[test]
@ -739,12 +703,12 @@ mod tests {
{ {
let params = "indexUids=1,hé"; let params = "indexUids=1,hé";
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err(); let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).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 (_). at `.indexUids`."); snapshot!(format!("{err}"), @"Invalid value in parameter `indexUids`: `hé` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).");
} }
{ {
let params = "indexUids=hé"; let params = "indexUids=hé";
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err(); let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).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 (_). at `.indexUids`."); snapshot!(format!("{err}"), @"Invalid value in parameter `indexUids`: `hé` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).");
} }
} }
@ -772,19 +736,19 @@ mod tests {
// Stars in uids not allowed // Stars in uids not allowed
let params = "uids=*"; let params = "uids=*";
let err = deserr_query_params::<TasksFilterQuery>(params).unwrap_err(); let err = deserr_query_params::<TasksFilterQuery>(params).unwrap_err();
snapshot!(format!("{err}"), @"invalid digit found in string at `.uids`."); snapshot!(format!("{err}"), @"Invalid value in parameter `uids`: could not parse `*` as a positive integer");
} }
{ {
// From not allowed in task deletion/cancelation queries // From not allowed in task deletion/cancelation queries
let params = "from=12"; let params = "from=12";
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err(); let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
snapshot!(format!("{err}"), @"Json deserialize error: unknown field `from`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``."); snapshot!(format!("{err}"), @"Unknown parameter `from`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`");
} }
{ {
// Limit not allowed in task deletion/cancelation queries // Limit not allowed in task deletion/cancelation queries
let params = "limit=12"; let params = "limit=12";
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err(); let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
snapshot!(format!("{err}"), @"Json deserialize error: unknown field `limit`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``."); snapshot!(format!("{err}"), @"Unknown parameter `limit`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`");
} }
} }
} }

View File

@ -3,10 +3,10 @@ use std::collections::{BTreeMap, BTreeSet, HashSet};
use std::str::FromStr; use std::str::FromStr;
use std::time::Instant; use std::time::Instant;
use deserr::DeserializeFromValue;
use either::Either; use either::Either;
use meilisearch_types::deserr::DeserializeFromValue;
use meilisearch_types::error::deserr_codes::*; use meilisearch_types::error::deserr_codes::*;
use meilisearch_types::error::DeserrError; use meilisearch_types::error::DeserrJsonError;
use meilisearch_types::settings::DEFAULT_PAGINATION_MAX_TOTAL_HITS; use meilisearch_types::settings::DEFAULT_PAGINATION_MAX_TOTAL_HITS;
use meilisearch_types::{milli, Document}; use meilisearch_types::{milli, Document};
use milli::tokenizer::TokenizerBuilder; use milli::tokenizer::TokenizerBuilder;
@ -30,41 +30,41 @@ pub const DEFAULT_HIGHLIGHT_PRE_TAG: fn() -> String = || "<em>".to_string();
pub const DEFAULT_HIGHLIGHT_POST_TAG: fn() -> String = || "</em>".to_string(); pub const DEFAULT_HIGHLIGHT_POST_TAG: fn() -> String = || "</em>".to_string();
#[derive(Debug, Clone, Default, PartialEq, DeserializeFromValue)] #[derive(Debug, Clone, Default, PartialEq, DeserializeFromValue)]
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)] #[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)]
pub struct SearchQuery { pub struct SearchQuery {
#[deserr(error = DeserrError<InvalidSearchQ>)] #[deserr(error = DeserrJsonError<InvalidSearchQ>)]
pub q: Option<String>, pub q: Option<String>,
#[deserr(error = DeserrError<InvalidSearchOffset>, default = DEFAULT_SEARCH_OFFSET())] #[deserr(error = DeserrJsonError<InvalidSearchOffset>, default = DEFAULT_SEARCH_OFFSET())]
pub offset: usize, pub offset: usize,
#[deserr(error = DeserrError<InvalidSearchLimit>, default = DEFAULT_SEARCH_LIMIT())] #[deserr(error = DeserrJsonError<InvalidSearchLimit>, default = DEFAULT_SEARCH_LIMIT())]
pub limit: usize, pub limit: usize,
#[deserr(error = DeserrError<InvalidSearchPage>)] #[deserr(error = DeserrJsonError<InvalidSearchPage>)]
pub page: Option<usize>, pub page: Option<usize>,
#[deserr(error = DeserrError<InvalidSearchHitsPerPage>)] #[deserr(error = DeserrJsonError<InvalidSearchHitsPerPage>)]
pub hits_per_page: Option<usize>, pub hits_per_page: Option<usize>,
#[deserr(error = DeserrError<InvalidSearchAttributesToRetrieve>)] #[deserr(error = DeserrJsonError<InvalidSearchAttributesToRetrieve>)]
pub attributes_to_retrieve: Option<BTreeSet<String>>, pub attributes_to_retrieve: Option<BTreeSet<String>>,
#[deserr(error = DeserrError<InvalidSearchAttributesToCrop>)] #[deserr(error = DeserrJsonError<InvalidSearchAttributesToCrop>)]
pub attributes_to_crop: Option<Vec<String>>, pub attributes_to_crop: Option<Vec<String>>,
#[deserr(error = DeserrError<InvalidSearchCropLength>, default = DEFAULT_CROP_LENGTH())] #[deserr(error = DeserrJsonError<InvalidSearchCropLength>, default = DEFAULT_CROP_LENGTH())]
pub crop_length: usize, pub crop_length: usize,
#[deserr(error = DeserrError<InvalidSearchAttributesToHighlight>)] #[deserr(error = DeserrJsonError<InvalidSearchAttributesToHighlight>)]
pub attributes_to_highlight: Option<HashSet<String>>, pub attributes_to_highlight: Option<HashSet<String>>,
#[deserr(error = DeserrError<InvalidSearchShowMatchesPosition>, default)] #[deserr(error = DeserrJsonError<InvalidSearchShowMatchesPosition>, default)]
pub show_matches_position: bool, pub show_matches_position: bool,
#[deserr(error = DeserrError<InvalidSearchFilter>)] #[deserr(error = DeserrJsonError<InvalidSearchFilter>)]
pub filter: Option<Value>, pub filter: Option<Value>,
#[deserr(error = DeserrError<InvalidSearchSort>)] #[deserr(error = DeserrJsonError<InvalidSearchSort>)]
pub sort: Option<Vec<String>>, pub sort: Option<Vec<String>>,
#[deserr(error = DeserrError<InvalidSearchFacets>)] #[deserr(error = DeserrJsonError<InvalidSearchFacets>)]
pub facets: Option<Vec<String>>, pub facets: Option<Vec<String>>,
#[deserr(error = DeserrError<InvalidSearchHighlightPreTag>, default = DEFAULT_HIGHLIGHT_PRE_TAG())] #[deserr(error = DeserrJsonError<InvalidSearchHighlightPreTag>, default = DEFAULT_HIGHLIGHT_PRE_TAG())]
pub highlight_pre_tag: String, pub highlight_pre_tag: String,
#[deserr(error = DeserrError<InvalidSearchHighlightPostTag>, default = DEFAULT_HIGHLIGHT_POST_TAG())] #[deserr(error = DeserrJsonError<InvalidSearchHighlightPostTag>, default = DEFAULT_HIGHLIGHT_POST_TAG())]
pub highlight_post_tag: String, pub highlight_post_tag: String,
#[deserr(error = DeserrError<InvalidSearchCropMarker>, default = DEFAULT_CROP_MARKER())] #[deserr(error = DeserrJsonError<InvalidSearchCropMarker>, default = DEFAULT_CROP_MARKER())]
pub crop_marker: String, pub crop_marker: String,
#[deserr(error = DeserrError<InvalidSearchMatchingStrategy>, default)] #[deserr(error = DeserrJsonError<InvalidSearchMatchingStrategy>, default)]
pub matching_strategy: MatchingStrategy, pub matching_strategy: MatchingStrategy,
} }

View File

@ -248,7 +248,7 @@ async fn error_add_api_key_missing_parameter() {
meili_snap::snapshot!(code, @"400 Bad Request"); meili_snap::snapshot!(code, @"400 Bad Request");
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###" meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
{ {
"message": "Json deserialize error: missing field `indexes` at ``", "message": "Missing field `indexes`",
"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"
@ -265,7 +265,7 @@ async fn error_add_api_key_missing_parameter() {
meili_snap::snapshot!(code, @"400 Bad Request"); meili_snap::snapshot!(code, @"400 Bad Request");
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###" meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
{ {
"message": "Json deserialize error: missing field `actions` at ``", "message": "Missing field `actions`",
"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"
@ -314,7 +314,7 @@ async fn error_add_api_key_invalid_parameters_description() {
meili_snap::snapshot!(code, @"400 Bad Request"); meili_snap::snapshot!(code, @"400 Bad Request");
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###" meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
{ {
"message": "invalid type: Map `{\"name\":\"products\"}`, expected a String at `.description`.", "message": "Invalid value type at `.description`: expected a string, but found an object: `{\"name\":\"products\"}`",
"code": "invalid_api_key_description", "code": "invalid_api_key_description",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-api-key-description" "link": "https://docs.meilisearch.com/errors#invalid-api-key-description"
@ -337,7 +337,7 @@ async fn error_add_api_key_invalid_parameters_name() {
meili_snap::snapshot!(code, @"400 Bad Request"); meili_snap::snapshot!(code, @"400 Bad Request");
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###" meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
{ {
"message": "invalid type: Map `{\"name\":\"products\"}`, expected a String at `.name`.", "message": "Invalid value type at `.name`: expected a string, but found an object: `{\"name\":\"products\"}`",
"code": "invalid_api_key_name", "code": "invalid_api_key_name",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-api-key-name" "link": "https://docs.meilisearch.com/errors#invalid-api-key-name"
@ -360,7 +360,7 @@ async fn error_add_api_key_invalid_parameters_indexes() {
meili_snap::snapshot!(code, @"400 Bad Request"); meili_snap::snapshot!(code, @"400 Bad Request");
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###" meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
{ {
"message": "invalid type: Map `{\"name\":\"products\"}`, expected a Sequence at `.indexes`.", "message": "Invalid value type at `.indexes`: expected an array, but found an object: `{\"name\":\"products\"}`",
"code": "invalid_api_key_indexes", "code": "invalid_api_key_indexes",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-api-key-indexes" "link": "https://docs.meilisearch.com/errors#invalid-api-key-indexes"
@ -386,7 +386,7 @@ async fn error_add_api_key_invalid_index_uids() {
meili_snap::snapshot!(code, @"400 Bad Request"); meili_snap::snapshot!(code, @"400 Bad Request");
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###" meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
{ {
"message": "`invalid index # / \\name with spaces` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). at `.indexes[0]`.", "message": "Invalid value at `.indexes[0]`: `invalid index # / \\name with spaces` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).",
"code": "invalid_api_key_indexes", "code": "invalid_api_key_indexes",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-api-key-indexes" "link": "https://docs.meilisearch.com/errors#invalid-api-key-indexes"
@ -411,7 +411,7 @@ async fn error_add_api_key_invalid_parameters_actions() {
meili_snap::snapshot!(code, @"400 Bad Request"); meili_snap::snapshot!(code, @"400 Bad Request");
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###" meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
{ {
"message": "invalid type: Map `{\"name\":\"products\"}`, expected a Sequence at `.actions`.", "message": "Invalid value type at `.actions`: expected an array, but found an object: `{\"name\":\"products\"}`",
"code": "invalid_api_key_actions", "code": "invalid_api_key_actions",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-api-key-actions" "link": "https://docs.meilisearch.com/errors#invalid-api-key-actions"
@ -431,7 +431,7 @@ async fn error_add_api_key_invalid_parameters_actions() {
meili_snap::snapshot!(code, @"400 Bad Request"); meili_snap::snapshot!(code, @"400 Bad Request");
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###" meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
{ {
"message": "Json deserialize error: unknown value `doc.add`, expected one of `*`, `search`, `documents.*`, `documents.add`, `documents.get`, `documents.delete`, `indexes.*`, `indexes.create`, `indexes.get`, `indexes.update`, `indexes.delete`, `indexes.swap`, `tasks.*`, `tasks.cancel`, `tasks.delete`, `tasks.get`, `settings.*`, `settings.get`, `settings.update`, `stats.*`, `stats.get`, `metrics.*`, `metrics.get`, `dumps.*`, `dumps.create`, `version`, `keys.create`, `keys.get`, `keys.update`, `keys.delete` at `.actions[0]`.", "message": "Unknown value `doc.add` at `.actions[0]`: expected one of `*`, `search`, `documents.*`, `documents.add`, `documents.get`, `documents.delete`, `indexes.*`, `indexes.create`, `indexes.get`, `indexes.update`, `indexes.delete`, `indexes.swap`, `tasks.*`, `tasks.cancel`, `tasks.delete`, `tasks.get`, `settings.*`, `settings.get`, `settings.update`, `stats.*`, `stats.get`, `metrics.*`, `metrics.get`, `dumps.*`, `dumps.create`, `version`, `keys.create`, `keys.get`, `keys.update`, `keys.delete`",
"code": "invalid_api_key_actions", "code": "invalid_api_key_actions",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-api-key-actions" "link": "https://docs.meilisearch.com/errors#invalid-api-key-actions"
@ -455,7 +455,7 @@ async fn error_add_api_key_invalid_parameters_expires_at() {
meili_snap::snapshot!(code, @"400 Bad Request"); meili_snap::snapshot!(code, @"400 Bad Request");
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###" meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
{ {
"message": "invalid type: Map `{\"name\":\"products\"}`, expected a String at `.expiresAt`.", "message": "Invalid value type at `.expiresAt`: expected a string, but found an object: `{\"name\":\"products\"}`",
"code": "invalid_api_key_expires_at", "code": "invalid_api_key_expires_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-api-key-expires-at" "link": "https://docs.meilisearch.com/errors#invalid-api-key-expires-at"
@ -478,7 +478,7 @@ async fn error_add_api_key_invalid_parameters_expires_at_in_the_past() {
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###" meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
{ {
"message": "`2010-11-13T00:00:00Z` is not a valid date. It should follow the RFC 3339 format to represents a date or datetime in the future or specified as a null value. e.g. 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'.\n at `.expiresAt`.", "message": "Invalid value at `.expiresAt`: `2010-11-13T00:00:00Z` is not a valid date. It should follow the RFC 3339 format to represents a date or datetime in the future or specified as a null value. e.g. 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'.\n",
"code": "invalid_api_key_expires_at", "code": "invalid_api_key_expires_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-api-key-expires-at" "link": "https://docs.meilisearch.com/errors#invalid-api-key-expires-at"
@ -503,7 +503,7 @@ async fn error_add_api_key_invalid_parameters_uid() {
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###" meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
{ {
"message": "invalid length: expected length 32 for simple format, found 13 at `.uid`.", "message": "Invalid value at `.uid`: invalid length: expected length 32 for simple format, found 13",
"code": "invalid_api_key_uid", "code": "invalid_api_key_uid",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-api-key-uid" "link": "https://docs.meilisearch.com/errors#invalid-api-key-uid"
@ -1403,7 +1403,7 @@ async fn error_patch_api_key_indexes() {
let (response, code) = server.patch_api_key(&uid, content).await; let (response, code) = server.patch_api_key(&uid, content).await;
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###" meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
{ {
"message": "Json deserialize error: unknown field `indexes`, expected one of `description`, `name` at ``.", "message": "Unknown field `indexes`: expected one of `description`, `name`",
"code": "immutable_api_key_indexes", "code": "immutable_api_key_indexes",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#immutable-api-key-indexes" "link": "https://docs.meilisearch.com/errors#immutable-api-key-indexes"
@ -1480,7 +1480,7 @@ async fn error_patch_api_key_actions() {
let (response, code) = server.patch_api_key(&uid, content).await; let (response, code) = server.patch_api_key(&uid, content).await;
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###" meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
{ {
"message": "Json deserialize error: unknown field `actions`, expected one of `description`, `name` at ``.", "message": "Unknown field `actions`: expected one of `description`, `name`",
"code": "immutable_api_key_actions", "code": "immutable_api_key_actions",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#immutable-api-key-actions" "link": "https://docs.meilisearch.com/errors#immutable-api-key-actions"
@ -1549,7 +1549,7 @@ async fn error_patch_api_key_expiration_date() {
let (response, code) = server.patch_api_key(&uid, content).await; let (response, code) = server.patch_api_key(&uid, content).await;
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###" meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
{ {
"message": "Json deserialize error: unknown field `expiresAt`, expected one of `description`, `name` at ``.", "message": "Unknown field `expiresAt`: expected one of `description`, `name`",
"code": "immutable_api_key_expires_at", "code": "immutable_api_key_expires_at",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#immutable-api-key-expires-at" "link": "https://docs.meilisearch.com/errors#immutable-api-key-expires-at"
@ -1670,7 +1670,7 @@ async fn error_patch_api_key_indexes_invalid_parameters() {
let (response, code) = server.patch_api_key(&uid, content).await; let (response, code) = server.patch_api_key(&uid, content).await;
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###" meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
{ {
"message": "invalid type: Integer `13`, expected a String at `.description`.", "message": "Invalid value type at `.description`: expected a string, but found a positive integer: `13`",
"code": "invalid_api_key_description", "code": "invalid_api_key_description",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-api-key-description" "link": "https://docs.meilisearch.com/errors#invalid-api-key-description"
@ -1686,7 +1686,7 @@ async fn error_patch_api_key_indexes_invalid_parameters() {
let (response, code) = server.patch_api_key(&uid, content).await; let (response, code) = server.patch_api_key(&uid, content).await;
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###" meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
{ {
"message": "invalid type: Integer `13`, expected a String at `.name`.", "message": "Invalid value type at `.name`: expected a string, but found a positive integer: `13`",
"code": "invalid_api_key_name", "code": "invalid_api_key_name",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-api-key-name" "link": "https://docs.meilisearch.com/errors#invalid-api-key-name"

View File

@ -289,8 +289,8 @@ impl Index<'_> {
eprintln!("Error with post search"); eprintln!("Error with post search");
resume_unwind(e); resume_unwind(e);
} }
let query = yaup::to_string(&query).unwrap();
let (response, code) = self.search_get(query).await; let (response, code) = self.search_get(&query).await;
if let Err(e) = catch_unwind(move || test(response, code)) { if let Err(e) = catch_unwind(move || test(response, code)) {
eprintln!("Error with get search"); eprintln!("Error with get search");
resume_unwind(e); resume_unwind(e);
@ -302,9 +302,8 @@ impl Index<'_> {
self.service.post_encoded(url, query, self.encoder).await self.service.post_encoded(url, query, self.encoder).await
} }
pub async fn search_get(&self, query: Value) -> (Value, StatusCode) { pub async fn search_get(&self, query: &str) -> (Value, StatusCode) {
let params = yaup::to_string(&query).unwrap(); let url = format!("/indexes/{}/search?{}", urlencode(self.uid.as_ref()), query);
let url = format!("/indexes/{}/search?{}", urlencode(self.uid.as_ref()), params);
self.service.get(url).await self.service.get(url).await
} }

View File

@ -132,8 +132,8 @@ impl Server {
self.service.get("/tasks").await self.service.get("/tasks").await
} }
pub async fn tasks_filter(&self, filter: Value) -> (Value, StatusCode) { pub async fn tasks_filter(&self, filter: &str) -> (Value, StatusCode) {
self.service.get(format!("/tasks?{}", yaup::to_string(&filter).unwrap())).await self.service.get(format!("/tasks?{}", filter)).await
} }
pub async fn get_dump_status(&self, uid: &str) -> (Value, StatusCode) { pub async fn get_dump_status(&self, uid: &str) -> (Value, StatusCode) {
@ -148,14 +148,12 @@ impl Server {
self.service.post("/swap-indexes", value).await self.service.post("/swap-indexes", value).await
} }
pub async fn cancel_tasks(&self, value: Value) -> (Value, StatusCode) { pub async fn cancel_tasks(&self, value: &str) -> (Value, StatusCode) {
self.service self.service.post(format!("/tasks/cancel?{}", value), json!(null)).await
.post(format!("/tasks/cancel?{}", yaup::to_string(&value).unwrap()), json!(null))
.await
} }
pub async fn delete_tasks(&self, value: Value) -> (Value, StatusCode) { pub async fn delete_tasks(&self, value: &str) -> (Value, StatusCode) {
self.service.delete(format!("/tasks?{}", yaup::to_string(&value).unwrap())).await self.service.delete(format!("/tasks?{}", value)).await
} }
pub async fn wait_task(&self, update_id: u64) -> Value { pub async fn wait_task(&self, update_id: u64) -> Value {

View File

@ -46,7 +46,7 @@ async fn search_bad_q() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at `.q`.", "message": "Invalid value type at `.q`: expected a string, but found an array: `[\"doggo\"]`",
"code": "invalid_search_q", "code": "invalid_search_q",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-q" "link": "https://docs.meilisearch.com/errors#invalid-search-q"
@ -64,18 +64,18 @@ async fn search_bad_offset() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Integer at `.offset`.", "message": "Invalid value type at `.offset`: expected a positive integer, but found a string: `\"doggo\"`",
"code": "invalid_search_offset", "code": "invalid_search_offset",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-offset" "link": "https://docs.meilisearch.com/errors#invalid-search-offset"
} }
"###); "###);
let (response, code) = index.search_get(json!({"offset": "doggo"})).await; let (response, code) = index.search_get("offset=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid digit found in string at `.offset`.", "message": "Invalid value in parameter `offset`: could not parse `doggo` as a positive integer",
"code": "invalid_search_offset", "code": "invalid_search_offset",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-offset" "link": "https://docs.meilisearch.com/errors#invalid-search-offset"
@ -92,18 +92,18 @@ async fn search_bad_limit() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Integer at `.limit`.", "message": "Invalid value type at `.limit`: expected a positive integer, but found a string: `\"doggo\"`",
"code": "invalid_search_limit", "code": "invalid_search_limit",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-limit" "link": "https://docs.meilisearch.com/errors#invalid-search-limit"
} }
"###); "###);
let (response, code) = index.search_get(json!({"limit": "doggo"})).await; let (response, code) = index.search_get("limit=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid digit found in string at `.limit`.", "message": "Invalid value in parameter `limit`: could not parse `doggo` as a positive integer",
"code": "invalid_search_limit", "code": "invalid_search_limit",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-limit" "link": "https://docs.meilisearch.com/errors#invalid-search-limit"
@ -120,18 +120,18 @@ async fn search_bad_page() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Integer at `.page`.", "message": "Invalid value type at `.page`: expected a positive integer, but found a string: `\"doggo\"`",
"code": "invalid_search_page", "code": "invalid_search_page",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-page" "link": "https://docs.meilisearch.com/errors#invalid-search-page"
} }
"###); "###);
let (response, code) = index.search_get(json!({"page": "doggo"})).await; let (response, code) = index.search_get("page=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid digit found in string at `.page`.", "message": "Invalid value in parameter `page`: could not parse `doggo` as a positive integer",
"code": "invalid_search_page", "code": "invalid_search_page",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-page" "link": "https://docs.meilisearch.com/errors#invalid-search-page"
@ -148,18 +148,18 @@ async fn search_bad_hits_per_page() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Integer at `.hitsPerPage`.", "message": "Invalid value type at `.hitsPerPage`: expected a positive integer, but found a string: `\"doggo\"`",
"code": "invalid_search_hits_per_page", "code": "invalid_search_hits_per_page",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-hits-per-page" "link": "https://docs.meilisearch.com/errors#invalid-search-hits-per-page"
} }
"###); "###);
let (response, code) = index.search_get(json!({"hitsPerPage": "doggo"})).await; let (response, code) = index.search_get("hitsPerPage=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid digit found in string at `.hitsPerPage`.", "message": "Invalid value in parameter `hitsPerPage`: could not parse `doggo` as a positive integer",
"code": "invalid_search_hits_per_page", "code": "invalid_search_hits_per_page",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-hits-per-page" "link": "https://docs.meilisearch.com/errors#invalid-search-hits-per-page"
@ -176,7 +176,7 @@ async fn search_bad_attributes_to_crop() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.attributesToCrop`.", "message": "Invalid value type at `.attributesToCrop`: expected an array, but found a string: `\"doggo\"`",
"code": "invalid_search_attributes_to_crop", "code": "invalid_search_attributes_to_crop",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-attributes-to-crop" "link": "https://docs.meilisearch.com/errors#invalid-search-attributes-to-crop"
@ -194,18 +194,18 @@ async fn search_bad_crop_length() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Integer at `.cropLength`.", "message": "Invalid value type at `.cropLength`: expected a positive integer, but found a string: `\"doggo\"`",
"code": "invalid_search_crop_length", "code": "invalid_search_crop_length",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-crop-length" "link": "https://docs.meilisearch.com/errors#invalid-search-crop-length"
} }
"###); "###);
let (response, code) = index.search_get(json!({"cropLength": "doggo"})).await; let (response, code) = index.search_get("cropLength=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid digit found in string at `.cropLength`.", "message": "Invalid value in parameter `cropLength`: could not parse `doggo` as a positive integer",
"code": "invalid_search_crop_length", "code": "invalid_search_crop_length",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-crop-length" "link": "https://docs.meilisearch.com/errors#invalid-search-crop-length"
@ -222,7 +222,7 @@ async fn search_bad_attributes_to_highlight() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.attributesToHighlight`.", "message": "Invalid value type at `.attributesToHighlight`: expected an array, but found a string: `\"doggo\"`",
"code": "invalid_search_attributes_to_highlight", "code": "invalid_search_attributes_to_highlight",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-attributes-to-highlight" "link": "https://docs.meilisearch.com/errors#invalid-search-attributes-to-highlight"
@ -266,7 +266,7 @@ async fn search_bad_sort() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.sort`.", "message": "Invalid value type at `.sort`: expected an array, but found a string: `\"doggo\"`",
"code": "invalid_search_sort", "code": "invalid_search_sort",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-sort" "link": "https://docs.meilisearch.com/errors#invalid-search-sort"
@ -284,18 +284,18 @@ async fn search_bad_show_matches_position() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Boolean at `.showMatchesPosition`.", "message": "Invalid value type at `.showMatchesPosition`: expected a boolean, but found a string: `\"doggo\"`",
"code": "invalid_search_show_matches_position", "code": "invalid_search_show_matches_position",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-show-matches-position" "link": "https://docs.meilisearch.com/errors#invalid-search-show-matches-position"
} }
"###); "###);
let (response, code) = index.search_get(json!({"showMatchesPosition": "doggo"})).await; let (response, code) = index.search_get("showMatchesPosition=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "provided string was not `true` or `false` at `.showMatchesPosition`.", "message": "Invalid value in parameter `showMatchesPosition`: provided string was not `true` or `false`",
"code": "invalid_search_show_matches_position", "code": "invalid_search_show_matches_position",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-show-matches-position" "link": "https://docs.meilisearch.com/errors#invalid-search-show-matches-position"
@ -312,7 +312,7 @@ async fn search_bad_facets() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.facets`.", "message": "Invalid value type at `.facets`: expected an array, but found a string: `\"doggo\"`",
"code": "invalid_search_facets", "code": "invalid_search_facets",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-facets" "link": "https://docs.meilisearch.com/errors#invalid-search-facets"
@ -330,7 +330,7 @@ async fn search_bad_highlight_pre_tag() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at `.highlightPreTag`.", "message": "Invalid value type at `.highlightPreTag`: expected a string, but found an array: `[\"doggo\"]`",
"code": "invalid_search_highlight_pre_tag", "code": "invalid_search_highlight_pre_tag",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-highlight-pre-tag" "link": "https://docs.meilisearch.com/errors#invalid-search-highlight-pre-tag"
@ -348,7 +348,7 @@ async fn search_bad_highlight_post_tag() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at `.highlightPostTag`.", "message": "Invalid value type at `.highlightPostTag`: expected a string, but found an array: `[\"doggo\"]`",
"code": "invalid_search_highlight_post_tag", "code": "invalid_search_highlight_post_tag",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-highlight-post-tag" "link": "https://docs.meilisearch.com/errors#invalid-search-highlight-post-tag"
@ -366,7 +366,7 @@ async fn search_bad_crop_marker() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at `.cropMarker`.", "message": "Invalid value type at `.cropMarker`: expected a string, but found an array: `[\"doggo\"]`",
"code": "invalid_search_crop_marker", "code": "invalid_search_crop_marker",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-crop-marker" "link": "https://docs.meilisearch.com/errors#invalid-search-crop-marker"
@ -384,18 +384,18 @@ async fn search_bad_matching_strategy() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Json deserialize error: unknown value `doggo`, expected one of `last`, `all` at `.matchingStrategy`.", "message": "Unknown value `doggo` at `.matchingStrategy`: expected one of `last`, `all`",
"code": "invalid_search_matching_strategy", "code": "invalid_search_matching_strategy",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-matching-strategy" "link": "https://docs.meilisearch.com/errors#invalid-search-matching-strategy"
} }
"###); "###);
let (response, code) = index.search_get(json!({"matchingStrategy": "doggo"})).await; let (response, code) = index.search_get("matchingStrategy=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Json deserialize error: unknown value `doggo`, expected one of `last`, `all` at `.matchingStrategy`.", "message": "Unknown value `doggo` for parameter `matchingStrategy`: expected one of `last`, `all`",
"code": "invalid_search_matching_strategy", "code": "invalid_search_matching_strategy",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-search-matching-strategy" "link": "https://docs.meilisearch.com/errors#invalid-search-matching-strategy"

View File

@ -12,7 +12,7 @@ async fn settings_bad_displayed_attributes() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.displayedAttributes`.", "message": "Invalid value type at `.displayedAttributes`: expected an array, but found a string: `\"doggo\"`",
"code": "invalid_settings_displayed_attributes", "code": "invalid_settings_displayed_attributes",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-displayed-attributes" "link": "https://docs.meilisearch.com/errors#invalid-settings-displayed-attributes"
@ -23,7 +23,7 @@ async fn settings_bad_displayed_attributes() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.", "message": "Invalid value type: expected an array, but found a string: `\"doggo\"`",
"code": "invalid_settings_displayed_attributes", "code": "invalid_settings_displayed_attributes",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-displayed-attributes" "link": "https://docs.meilisearch.com/errors#invalid-settings-displayed-attributes"
@ -40,7 +40,7 @@ async fn settings_bad_searchable_attributes() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.searchableAttributes`.", "message": "Invalid value type at `.searchableAttributes`: expected an array, but found a string: `\"doggo\"`",
"code": "invalid_settings_searchable_attributes", "code": "invalid_settings_searchable_attributes",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-searchable-attributes" "link": "https://docs.meilisearch.com/errors#invalid-settings-searchable-attributes"
@ -51,7 +51,7 @@ async fn settings_bad_searchable_attributes() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.", "message": "Invalid value type: expected an array, but found a string: `\"doggo\"`",
"code": "invalid_settings_searchable_attributes", "code": "invalid_settings_searchable_attributes",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-searchable-attributes" "link": "https://docs.meilisearch.com/errors#invalid-settings-searchable-attributes"
@ -68,7 +68,7 @@ async fn settings_bad_filterable_attributes() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.filterableAttributes`.", "message": "Invalid value type at `.filterableAttributes`: expected an array, but found a string: `\"doggo\"`",
"code": "invalid_settings_filterable_attributes", "code": "invalid_settings_filterable_attributes",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-filterable-attributes" "link": "https://docs.meilisearch.com/errors#invalid-settings-filterable-attributes"
@ -79,7 +79,7 @@ async fn settings_bad_filterable_attributes() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.", "message": "Invalid value type: expected an array, but found a string: `\"doggo\"`",
"code": "invalid_settings_filterable_attributes", "code": "invalid_settings_filterable_attributes",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-filterable-attributes" "link": "https://docs.meilisearch.com/errors#invalid-settings-filterable-attributes"
@ -96,7 +96,7 @@ async fn settings_bad_sortable_attributes() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.sortableAttributes`.", "message": "Invalid value type at `.sortableAttributes`: expected an array, but found a string: `\"doggo\"`",
"code": "invalid_settings_sortable_attributes", "code": "invalid_settings_sortable_attributes",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-sortable-attributes" "link": "https://docs.meilisearch.com/errors#invalid-settings-sortable-attributes"
@ -107,7 +107,7 @@ async fn settings_bad_sortable_attributes() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.", "message": "Invalid value type: expected an array, but found a string: `\"doggo\"`",
"code": "invalid_settings_sortable_attributes", "code": "invalid_settings_sortable_attributes",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-sortable-attributes" "link": "https://docs.meilisearch.com/errors#invalid-settings-sortable-attributes"
@ -124,7 +124,7 @@ async fn settings_bad_ranking_rules() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.rankingRules`.", "message": "Invalid value type at `.rankingRules`: expected an array, but found a string: `\"doggo\"`",
"code": "invalid_settings_ranking_rules", "code": "invalid_settings_ranking_rules",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-ranking-rules" "link": "https://docs.meilisearch.com/errors#invalid-settings-ranking-rules"
@ -135,7 +135,7 @@ async fn settings_bad_ranking_rules() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.", "message": "Invalid value type: expected an array, but found a string: `\"doggo\"`",
"code": "invalid_settings_ranking_rules", "code": "invalid_settings_ranking_rules",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-ranking-rules" "link": "https://docs.meilisearch.com/errors#invalid-settings-ranking-rules"
@ -152,7 +152,7 @@ async fn settings_bad_stop_words() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.stopWords`.", "message": "Invalid value type at `.stopWords`: expected an array, but found a string: `\"doggo\"`",
"code": "invalid_settings_stop_words", "code": "invalid_settings_stop_words",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-stop-words" "link": "https://docs.meilisearch.com/errors#invalid-settings-stop-words"
@ -163,7 +163,7 @@ async fn settings_bad_stop_words() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.", "message": "Invalid value type: expected an array, but found a string: `\"doggo\"`",
"code": "invalid_settings_stop_words", "code": "invalid_settings_stop_words",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-stop-words" "link": "https://docs.meilisearch.com/errors#invalid-settings-stop-words"
@ -180,7 +180,7 @@ async fn settings_bad_synonyms() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Map at `.synonyms`.", "message": "Invalid value type at `.synonyms`: expected an object, but found a string: `\"doggo\"`",
"code": "invalid_settings_synonyms", "code": "invalid_settings_synonyms",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-synonyms" "link": "https://docs.meilisearch.com/errors#invalid-settings-synonyms"
@ -191,7 +191,7 @@ async fn settings_bad_synonyms() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Map at ``.", "message": "Invalid value type: expected an object, but found a string: `\"doggo\"`",
"code": "invalid_settings_synonyms", "code": "invalid_settings_synonyms",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-synonyms" "link": "https://docs.meilisearch.com/errors#invalid-settings-synonyms"
@ -208,7 +208,7 @@ async fn settings_bad_distinct_attribute() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at `.distinctAttribute`.", "message": "Invalid value type at `.distinctAttribute`: expected a string, but found an array: `[\"doggo\"]`",
"code": "invalid_settings_distinct_attribute", "code": "invalid_settings_distinct_attribute",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-distinct-attribute" "link": "https://docs.meilisearch.com/errors#invalid-settings-distinct-attribute"
@ -219,7 +219,7 @@ async fn settings_bad_distinct_attribute() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at ``.", "message": "Invalid value type: expected a string, but found an array: `[\"doggo\"]`",
"code": "invalid_settings_distinct_attribute", "code": "invalid_settings_distinct_attribute",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-distinct-attribute" "link": "https://docs.meilisearch.com/errors#invalid-settings-distinct-attribute"
@ -236,7 +236,7 @@ async fn settings_bad_typo_tolerance() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Map at `.typoTolerance`.", "message": "Invalid value type at `.typoTolerance`: expected an object, but found a string: `\"doggo\"`",
"code": "invalid_settings_typo_tolerance", "code": "invalid_settings_typo_tolerance",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-typo-tolerance" "link": "https://docs.meilisearch.com/errors#invalid-settings-typo-tolerance"
@ -247,7 +247,7 @@ async fn settings_bad_typo_tolerance() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Map at ``.", "message": "Invalid value type: expected an object, but found a string: `\"doggo\"`",
"code": "invalid_settings_typo_tolerance", "code": "invalid_settings_typo_tolerance",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-typo-tolerance" "link": "https://docs.meilisearch.com/errors#invalid-settings-typo-tolerance"
@ -264,7 +264,7 @@ async fn settings_bad_faceting() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Map at `.faceting`.", "message": "Invalid value type at `.faceting`: expected an object, but found a string: `\"doggo\"`",
"code": "invalid_settings_faceting", "code": "invalid_settings_faceting",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-faceting" "link": "https://docs.meilisearch.com/errors#invalid-settings-faceting"
@ -275,7 +275,7 @@ async fn settings_bad_faceting() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Map at ``.", "message": "Invalid value type: expected an object, but found a string: `\"doggo\"`",
"code": "invalid_settings_faceting", "code": "invalid_settings_faceting",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-faceting" "link": "https://docs.meilisearch.com/errors#invalid-settings-faceting"
@ -292,7 +292,7 @@ async fn settings_bad_pagination() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Map at `.pagination`.", "message": "Invalid value type at `.pagination`: expected an object, but found a string: `\"doggo\"`",
"code": "invalid_settings_pagination", "code": "invalid_settings_pagination",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-pagination" "link": "https://docs.meilisearch.com/errors#invalid-settings-pagination"
@ -303,7 +303,7 @@ async fn settings_bad_pagination() {
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid type: String `\"doggo\"`, expected a Map at ``.", "message": "Invalid value type: expected an object, but found a string: `\"doggo\"`",
"code": "invalid_settings_pagination", "code": "invalid_settings_pagination",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-pagination" "link": "https://docs.meilisearch.com/errors#invalid-settings-pagination"

View File

@ -282,7 +282,7 @@ async fn error_set_invalid_ranking_rules() {
meili_snap::snapshot!(code, @"400 Bad Request"); meili_snap::snapshot!(code, @"400 Bad Request");
meili_snap::snapshot!(meili_snap::json_string!(response), @r###" meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
{ {
"message": "`manyTheFish` ranking rule is invalid. Valid ranking rules are words, typo, sort, proximity, attribute, exactness and custom ranking rules. at `.rankingRules[0]`.", "message": "Invalid value at `.rankingRules[0]`: `manyTheFish` ranking rule is invalid. Valid ranking rules are words, typo, sort, proximity, attribute, exactness and custom ranking rules.",
"code": "invalid_settings_ranking_rules", "code": "invalid_settings_ranking_rules",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-ranking-rules" "link": "https://docs.meilisearch.com/errors#invalid-settings-ranking-rules"

View File

@ -1,5 +1,4 @@
use meili_snap::*; use meili_snap::*;
use serde_json::json;
use crate::common::Server; use crate::common::Server;
@ -7,33 +6,44 @@ use crate::common::Server;
async fn task_bad_uids() { async fn task_bad_uids() {
let server = Server::new().await; let server = Server::new().await;
let (response, code) = server.tasks_filter(json!({"uids": "doggo"})).await; let (response, code) = server.tasks_filter("uids=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid digit found in string at `.uids`.", "message": "Invalid value in parameter `uids`: could not parse `doggo` as a positive integer",
"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"
} }
"###); "###);
let (response, code) = server.cancel_tasks(json!({"uids": "doggo"})).await; let (response, code) = server.cancel_tasks("uids=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid digit found in string at `.uids`.", "message": "Invalid value in parameter `uids`: could not parse `doggo` as a positive integer",
"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"
} }
"###); "###);
let (response, code) = server.delete_tasks(json!({"uids": "doggo"})).await; let (response, code) = server.delete_tasks("uids=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid digit found in string at `.uids`.", "message": "Invalid value in parameter `uids`: could not parse `doggo` as a positive integer",
"code": "invalid_task_uids",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-uids"
}
"###);
let (response, code) = server.delete_tasks("uids=1,dogo").await;
snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###"
{
"message": "Invalid value in parameter `uids`: could not parse `dogo` as a positive integer",
"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"
@ -45,33 +55,33 @@ async fn task_bad_uids() {
async fn task_bad_canceled_by() { async fn task_bad_canceled_by() {
let server = Server::new().await; let server = Server::new().await;
let (response, code) = server.tasks_filter(json!({"canceledBy": "doggo"})).await; let (response, code) = server.tasks_filter("canceledBy=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid digit found in string at `.canceledBy`.", "message": "Invalid value in parameter `canceledBy`: could not parse `doggo` as a positive integer",
"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"
} }
"###); "###);
let (response, code) = server.cancel_tasks(json!({"canceledBy": "doggo"})).await; let (response, code) = server.cancel_tasks("canceledBy=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid digit found in string at `.canceledBy`.", "message": "Invalid value in parameter `canceledBy`: could not parse `doggo` as a positive integer",
"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"
} }
"###); "###);
let (response, code) = server.delete_tasks(json!({"canceledBy": "doggo"})).await; let (response, code) = server.delete_tasks("canceledBy=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid digit found in string at `.canceledBy`.", "message": "Invalid value in parameter `canceledBy`: could not parse `doggo` as a positive integer",
"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"
@ -83,33 +93,33 @@ async fn task_bad_canceled_by() {
async fn task_bad_types() { async fn task_bad_types() {
let server = Server::new().await; let server = Server::new().await;
let (response, code) = server.tasks_filter(json!({"types": "doggo"})).await; let (response, code) = server.tasks_filter("types=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is not a type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`. at `.types`.", "message": "Invalid value in parameter `types`: `doggo` is not a valid task type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`.",
"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"
} }
"###); "###);
let (response, code) = server.cancel_tasks(json!({"types": "doggo"})).await; let (response, code) = server.cancel_tasks("types=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is not a type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`. at `.types`.", "message": "Invalid value in parameter `types`: `doggo` is not a valid task type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`.",
"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"
} }
"###); "###);
let (response, code) = server.delete_tasks(json!({"types": "doggo"})).await; let (response, code) = server.delete_tasks("types=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is not a type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`. at `.types`.", "message": "Invalid value in parameter `types`: `doggo` is not a valid task type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`.",
"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"
@ -121,33 +131,33 @@ async fn task_bad_types() {
async fn task_bad_statuses() { async fn task_bad_statuses() {
let server = Server::new().await; let server = Server::new().await;
let (response, code) = server.tasks_filter(json!({"statuses": "doggo"})).await; let (response, code) = server.tasks_filter("statuses=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is not a status. Available status are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. at `.statuses`.", "message": "Invalid value in parameter `statuses`: `doggo` is not a valid task status. Available statuses are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`.",
"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"
} }
"###); "###);
let (response, code) = server.cancel_tasks(json!({"statuses": "doggo"})).await; let (response, code) = server.cancel_tasks("statuses=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is not a status. Available status are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. at `.statuses`.", "message": "Invalid value in parameter `statuses`: `doggo` is not a valid task status. Available statuses are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`.",
"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"
} }
"###); "###);
let (response, code) = server.delete_tasks(json!({"statuses": "doggo"})).await; let (response, code) = server.delete_tasks("statuses=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is not a status. Available status are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. at `.statuses`.", "message": "Invalid value in parameter `statuses`: `doggo` is not a valid task status. Available statuses are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`.",
"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"
@ -159,33 +169,33 @@ async fn task_bad_statuses() {
async fn task_bad_index_uids() { async fn task_bad_index_uids() {
let server = Server::new().await; let server = Server::new().await;
let (response, code) = server.tasks_filter(json!({"indexUids": "the good doggo"})).await; let (response, code) = server.tasks_filter("indexUids=the%20good%20doggo").await;
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 (_). at `.indexUids`.", "message": "Invalid value in parameter `indexUids`: `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 (_).",
"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"
} }
"###); "###);
let (response, code) = server.cancel_tasks(json!({"indexUids": "the good doggo"})).await; let (response, code) = server.cancel_tasks("indexUids=the%20good%20doggo").await;
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 (_). at `.indexUids`.", "message": "Invalid value in parameter `indexUids`: `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 (_).",
"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"
} }
"###); "###);
let (response, code) = server.delete_tasks(json!({"indexUids": "the good doggo"})).await; let (response, code) = server.delete_tasks("indexUids=the%20good%20doggo").await;
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 (_). at `.indexUids`.", "message": "Invalid value in parameter `indexUids`: `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 (_).",
"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"
@ -197,33 +207,33 @@ async fn task_bad_index_uids() {
async fn task_bad_limit() { async fn task_bad_limit() {
let server = Server::new().await; let server = Server::new().await;
let (response, code) = server.tasks_filter(json!({"limit": "doggo"})).await; let (response, code) = server.tasks_filter("limit=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid digit found in string at `.limit`.", "message": "Invalid value in parameter `limit`: could not parse `doggo` as a positive integer",
"code": "invalid_task_limit", "code": "invalid_task_limit",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-limit" "link": "https://docs.meilisearch.com/errors#invalid-task-limit"
} }
"###); "###);
let (response, code) = server.cancel_tasks(json!({"limit": "doggo"})).await; let (response, code) = server.cancel_tasks("limit=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Json deserialize error: unknown field `limit`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.", "message": "Unknown parameter `limit`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`",
"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"
} }
"###); "###);
let (response, code) = server.delete_tasks(json!({"limit": "doggo"})).await; let (response, code) = server.delete_tasks("limit=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Json deserialize error: unknown field `limit`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.", "message": "Unknown parameter `limit`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`",
"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"
@ -235,33 +245,33 @@ async fn task_bad_limit() {
async fn task_bad_from() { async fn task_bad_from() {
let server = Server::new().await; let server = Server::new().await;
let (response, code) = server.tasks_filter(json!({"from": "doggo"})).await; let (response, code) = server.tasks_filter("from=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "invalid digit found in string at `.from`.", "message": "Invalid value in parameter `from`: could not parse `doggo` as a positive integer",
"code": "invalid_task_from", "code": "invalid_task_from",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-from" "link": "https://docs.meilisearch.com/errors#invalid-task-from"
} }
"###); "###);
let (response, code) = server.cancel_tasks(json!({"from": "doggo"})).await; let (response, code) = server.cancel_tasks("from=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Json deserialize error: unknown field `from`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.", "message": "Unknown parameter `from`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`",
"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"
} }
"###); "###);
let (response, code) = server.delete_tasks(json!({"from": "doggo"})).await; let (response, code) = server.delete_tasks("from=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "Json deserialize error: unknown field `from`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.", "message": "Unknown parameter `from`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`",
"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"
@ -273,33 +283,33 @@ async fn task_bad_from() {
async fn task_bad_after_enqueued_at() { async fn task_bad_after_enqueued_at() {
let server = Server::new().await; let server = Server::new().await;
let (response, code) = server.tasks_filter(json!({"afterEnqueuedAt": "doggo"})).await; let (response, code) = server.tasks_filter("afterEnqueuedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterEnqueuedAt`.", "message": "Invalid value in parameter `afterEnqueuedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
} }
"###); "###);
let (response, code) = server.cancel_tasks(json!({"afterEnqueuedAt": "doggo"})).await; let (response, code) = server.cancel_tasks("afterEnqueuedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterEnqueuedAt`.", "message": "Invalid value in parameter `afterEnqueuedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
} }
"###); "###);
let (response, code) = server.delete_tasks(json!({"afterEnqueuedAt": "doggo"})).await; let (response, code) = server.delete_tasks("afterEnqueuedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterEnqueuedAt`.", "message": "Invalid value in parameter `afterEnqueuedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
@ -311,33 +321,33 @@ async fn task_bad_after_enqueued_at() {
async fn task_bad_before_enqueued_at() { async fn task_bad_before_enqueued_at() {
let server = Server::new().await; let server = Server::new().await;
let (response, code) = server.tasks_filter(json!({"beforeEnqueuedAt": "doggo"})).await; let (response, code) = server.tasks_filter("beforeEnqueuedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeEnqueuedAt`.", "message": "Invalid value in parameter `beforeEnqueuedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
} }
"###); "###);
let (response, code) = server.cancel_tasks(json!({"beforeEnqueuedAt": "doggo"})).await; let (response, code) = server.cancel_tasks("beforeEnqueuedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeEnqueuedAt`.", "message": "Invalid value in parameter `beforeEnqueuedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
} }
"###); "###);
let (response, code) = server.delete_tasks(json!({"beforeEnqueuedAt": "doggo"})).await; let (response, code) = server.delete_tasks("beforeEnqueuedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeEnqueuedAt`.", "message": "Invalid value in parameter `beforeEnqueuedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
@ -349,33 +359,33 @@ async fn task_bad_before_enqueued_at() {
async fn task_bad_after_started_at() { async fn task_bad_after_started_at() {
let server = Server::new().await; let server = Server::new().await;
let (response, code) = server.tasks_filter(json!({"afterStartedAt": "doggo"})).await; let (response, code) = server.tasks_filter("afterStartedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterStartedAt`.", "message": "Invalid value in parameter `afterStartedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
} }
"###); "###);
let (response, code) = server.cancel_tasks(json!({"afterStartedAt": "doggo"})).await; let (response, code) = server.cancel_tasks("afterStartedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterStartedAt`.", "message": "Invalid value in parameter `afterStartedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
} }
"###); "###);
let (response, code) = server.delete_tasks(json!({"afterStartedAt": "doggo"})).await; let (response, code) = server.delete_tasks("afterStartedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterStartedAt`.", "message": "Invalid value in parameter `afterStartedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
@ -387,33 +397,33 @@ async fn task_bad_after_started_at() {
async fn task_bad_before_started_at() { async fn task_bad_before_started_at() {
let server = Server::new().await; let server = Server::new().await;
let (response, code) = server.tasks_filter(json!({"beforeStartedAt": "doggo"})).await; let (response, code) = server.tasks_filter("beforeStartedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeStartedAt`.", "message": "Invalid value in parameter `beforeStartedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
} }
"###); "###);
let (response, code) = server.cancel_tasks(json!({"beforeStartedAt": "doggo"})).await; let (response, code) = server.cancel_tasks("beforeStartedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeStartedAt`.", "message": "Invalid value in parameter `beforeStartedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
} }
"###); "###);
let (response, code) = server.delete_tasks(json!({"beforeStartedAt": "doggo"})).await; let (response, code) = server.delete_tasks("beforeStartedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeStartedAt`.", "message": "Invalid value in parameter `beforeStartedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
@ -425,33 +435,33 @@ async fn task_bad_before_started_at() {
async fn task_bad_after_finished_at() { async fn task_bad_after_finished_at() {
let server = Server::new().await; let server = Server::new().await;
let (response, code) = server.tasks_filter(json!({"afterFinishedAt": "doggo"})).await; let (response, code) = server.tasks_filter("afterFinishedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterFinishedAt`.", "message": "Invalid value in parameter `afterFinishedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
} }
"###); "###);
let (response, code) = server.cancel_tasks(json!({"afterFinishedAt": "doggo"})).await; let (response, code) = server.cancel_tasks("afterFinishedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterFinishedAt`.", "message": "Invalid value in parameter `afterFinishedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
} }
"###); "###);
let (response, code) = server.delete_tasks(json!({"afterFinishedAt": "doggo"})).await; let (response, code) = server.delete_tasks("afterFinishedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterFinishedAt`.", "message": "Invalid value in parameter `afterFinishedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
@ -463,33 +473,33 @@ async fn task_bad_after_finished_at() {
async fn task_bad_before_finished_at() { async fn task_bad_before_finished_at() {
let server = Server::new().await; let server = Server::new().await;
let (response, code) = server.tasks_filter(json!({"beforeFinishedAt": "doggo"})).await; let (response, code) = server.tasks_filter("beforeFinishedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeFinishedAt`.", "message": "Invalid value in parameter `beforeFinishedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
} }
"###); "###);
let (response, code) = server.cancel_tasks(json!({"beforeFinishedAt": "doggo"})).await; let (response, code) = server.cancel_tasks("beforeFinishedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeFinishedAt`.", "message": "Invalid value in parameter `beforeFinishedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
} }
"###); "###);
let (response, code) = server.delete_tasks(json!({"beforeFinishedAt": "doggo"})).await; let (response, code) = server.delete_tasks("beforeFinishedAt=doggo").await;
snapshot!(code, @"400 Bad Request"); snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###" snapshot!(json_string!(response), @r###"
{ {
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeFinishedAt`.", "message": "Invalid value in parameter `beforeFinishedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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

@ -179,44 +179,44 @@ async fn list_tasks_status_and_type_filtered() {
async fn get_task_filter_error() { async fn get_task_filter_error() {
let server = Server::new().await; let server = Server::new().await;
let (response, code) = server.tasks_filter(json!( { "lol": "pied" })).await; let (response, code) = server.tasks_filter("lol=pied").await;
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": "Json deserialize error: unknown field `lol`, expected one of `limit`, `from`, `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.", "message": "Unknown parameter `lol`: expected one of `limit`, `from`, `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`",
"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"
} }
"###); "###);
let (response, code) = server.tasks_filter(json!( { "uids": "pied" })).await; let (response, code) = server.tasks_filter("uids=pied").await;
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": "invalid digit found in string at `.uids`.", "message": "Invalid value in parameter `uids`: could not parse `pied` as a positive integer",
"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"
} }
"###); "###);
let (response, code) = server.tasks_filter(json!( { "from": "pied" })).await; let (response, code) = server.tasks_filter("from=pied").await;
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": "invalid digit found in string at `.from`.", "message": "Invalid value in parameter `from`: could not parse `pied` as a positive integer",
"code": "invalid_task_from", "code": "invalid_task_from",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-task-from" "link": "https://docs.meilisearch.com/errors#invalid-task-from"
} }
"###); "###);
let (response, code) = server.tasks_filter(json!( { "beforeStartedAt": "pied" })).await; let (response, code) = server.tasks_filter("beforeStartedAt=pied").await;
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": "`pied` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeStartedAt`.", "message": "Invalid value in parameter `beforeStartedAt`: `pied` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
"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"
@ -228,7 +228,7 @@ async fn get_task_filter_error() {
async fn delete_task_filter_error() { async fn delete_task_filter_error() {
let server = Server::new().await; let server = Server::new().await;
let (response, code) = server.delete_tasks(json!(null)).await; let (response, code) = server.delete_tasks("").await;
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###"
{ {
@ -239,22 +239,22 @@ async fn delete_task_filter_error() {
} }
"###); "###);
let (response, code) = server.delete_tasks(json!({ "lol": "pied" })).await; let (response, code) = server.delete_tasks("lol=pied").await;
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": "Json deserialize error: unknown field `lol`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.", "message": "Unknown parameter `lol`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`",
"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"
} }
"###); "###);
let (response, code) = server.delete_tasks(json!({ "uids": "pied" })).await; let (response, code) = server.delete_tasks("uids=pied").await;
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": "invalid digit found in string at `.uids`.", "message": "Invalid value in parameter `uids`: could not parse `pied` as a positive integer",
"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"
@ -266,7 +266,7 @@ async fn delete_task_filter_error() {
async fn cancel_task_filter_error() { async fn cancel_task_filter_error() {
let server = Server::new().await; let server = Server::new().await;
let (response, code) = server.cancel_tasks(json!(null)).await; let (response, code) = server.cancel_tasks("").await;
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###"
{ {
@ -277,22 +277,22 @@ async fn cancel_task_filter_error() {
} }
"###); "###);
let (response, code) = server.cancel_tasks(json!({ "lol": "pied" })).await; let (response, code) = server.cancel_tasks("lol=pied").await;
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": "Json deserialize error: unknown field `lol`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.", "message": "Unknown parameter `lol`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`",
"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"
} }
"###); "###);
let (response, code) = server.cancel_tasks(json!({ "uids": "pied" })).await; let (response, code) = server.cancel_tasks("uids=pied").await;
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": "invalid digit found in string at `.uids`.", "message": "Invalid value in parameter `uids`: could not parse `pied` as a positive integer",
"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"
@ -523,7 +523,7 @@ async fn test_summarized_settings_update() {
meili_snap::snapshot!(code, @"400 Bad Request"); meili_snap::snapshot!(code, @"400 Bad Request");
meili_snap::snapshot!(meili_snap::json_string!(response), @r###" meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
{ {
"message": "`custom` ranking rule is invalid. Valid ranking rules are words, typo, sort, proximity, attribute, exactness and custom ranking rules. at `.rankingRules[0]`.", "message": "Invalid value at `.rankingRules[0]`: `custom` ranking rule is invalid. Valid ranking rules are words, typo, sort, proximity, attribute, exactness and custom ranking rules.",
"code": "invalid_settings_ranking_rules", "code": "invalid_settings_ranking_rules",
"type": "invalid_request", "type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid-settings-ranking-rules" "link": "https://docs.meilisearch.com/errors#invalid-settings-ranking-rules"
@ -899,7 +899,7 @@ async fn test_summarized_task_cancelation() {
// to avoid being flaky we're only going to cancel an already finished task :( // to avoid being flaky we're only going to cancel an already finished task :(
index.create(None).await; index.create(None).await;
index.wait_task(0).await; index.wait_task(0).await;
server.cancel_tasks(json!({ "uids": [0] })).await; server.cancel_tasks("uids=0").await;
index.wait_task(1).await; index.wait_task(1).await;
let (task, _) = index.get_task(1).await; let (task, _) = index.get_task(1).await;
assert_json_snapshot!(task, assert_json_snapshot!(task,
@ -932,7 +932,7 @@ async fn test_summarized_task_deletion() {
// to avoid being flaky we're only going to delete an already finished task :( // to avoid being flaky we're only going to delete an already finished task :(
index.create(None).await; index.create(None).await;
index.wait_task(0).await; index.wait_task(0).await;
server.delete_tasks(json!({ "uids": [0] })).await; server.delete_tasks("uids=0").await;
index.wait_task(1).await; index.wait_task(1).await;
let (task, _) = index.get_task(1).await; let (task, _) = index.get_task(1).await;
assert_json_snapshot!(task, assert_json_snapshot!(task,