mirror of
https://github.com/meilisearch/meilisearch.git
synced 2025-07-27 16:51:01 +00:00
Merge remote-tracking branch 'origin/release-v1.0.0' into import-milli
This commit is contained in:
@ -19,7 +19,7 @@ byte-unit = { version = "4.0.14", default-features = false, features = ["std", "
|
||||
bytes = "1.2.1"
|
||||
clap = { version = "4.0.9", features = ["derive", "env"] }
|
||||
crossbeam-channel = "0.5.6"
|
||||
deserr = { version = "0.1.2", features = ["serde-json"] }
|
||||
deserr = "0.1.4"
|
||||
dump = { path = "../dump" }
|
||||
either = "1.8.0"
|
||||
env_logger = "0.9.1"
|
||||
@ -109,5 +109,5 @@ japanese = ["meilisearch-types/japanese"]
|
||||
thai = ["meilisearch-types/thai"]
|
||||
|
||||
[package.metadata.mini-dashboard]
|
||||
assets-url = "https://github.com/meilisearch/mini-dashboard/releases/download/v0.2.4/build.zip"
|
||||
sha1 = "b53c2edb51d4ce1984d5586333b91c4ad3a1b4e4"
|
||||
assets-url = "https://github.com/meilisearch/mini-dashboard/releases/download/v0.2.5/build.zip"
|
||||
sha1 = "6fe959b78511b32e9ff857fd9fd31740633b9fce"
|
||||
|
@ -7,7 +7,7 @@ use serde_json::Value;
|
||||
|
||||
use super::{find_user_id, Analytics, DocumentDeletionKind};
|
||||
use crate::routes::indexes::documents::UpdateDocumentsQuery;
|
||||
use crate::routes::tasks::TasksFilterQueryRaw;
|
||||
use crate::routes::tasks::TasksFilterQuery;
|
||||
use crate::Opt;
|
||||
|
||||
pub struct MockAnalytics {
|
||||
@ -58,6 +58,6 @@ impl Analytics for MockAnalytics {
|
||||
_request: &HttpRequest,
|
||||
) {
|
||||
}
|
||||
fn get_tasks(&self, _query: &TasksFilterQueryRaw, _request: &HttpRequest) {}
|
||||
fn get_tasks(&self, _query: &TasksFilterQuery, _request: &HttpRequest) {}
|
||||
fn health_seen(&self, _request: &HttpRequest) {}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use platform_dirs::AppDirs;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::routes::indexes::documents::UpdateDocumentsQuery;
|
||||
use crate::routes::tasks::TasksFilterQueryRaw;
|
||||
use crate::routes::tasks::TasksFilterQuery;
|
||||
|
||||
// if we are in debug mode OR the analytics feature is disabled
|
||||
// the `SegmentAnalytics` point to the mock instead of the real analytics
|
||||
@ -94,7 +94,7 @@ pub trait Analytics: Sync + Send {
|
||||
);
|
||||
|
||||
// this method should be called to aggregate the get tasks requests.
|
||||
fn get_tasks(&self, query: &TasksFilterQueryRaw, request: &HttpRequest);
|
||||
fn get_tasks(&self, query: &TasksFilterQuery, request: &HttpRequest);
|
||||
|
||||
// this method should be called to aggregate a add documents request
|
||||
fn health_seen(&self, request: &HttpRequest);
|
||||
|
@ -27,7 +27,7 @@ use super::{config_user_id_path, DocumentDeletionKind, MEILISEARCH_CONFIG_PATH};
|
||||
use crate::analytics::Analytics;
|
||||
use crate::option::{default_http_addr, IndexerOpts, MaxMemory, MaxThreads, ScheduleSnapshot};
|
||||
use crate::routes::indexes::documents::UpdateDocumentsQuery;
|
||||
use crate::routes::tasks::TasksFilterQueryRaw;
|
||||
use crate::routes::tasks::TasksFilterQuery;
|
||||
use crate::routes::{create_all_stats, Stats};
|
||||
use crate::search::{
|
||||
SearchQuery, SearchResult, DEFAULT_CROP_LENGTH, DEFAULT_CROP_MARKER,
|
||||
@ -195,7 +195,7 @@ impl super::Analytics for SegmentAnalytics {
|
||||
let _ = self.sender.try_send(AnalyticsMsg::AggregateUpdateDocuments(aggregate));
|
||||
}
|
||||
|
||||
fn get_tasks(&self, query: &TasksFilterQueryRaw, request: &HttpRequest) {
|
||||
fn get_tasks(&self, query: &TasksFilterQuery, request: &HttpRequest) {
|
||||
let aggregate = TasksAggregator::from_query(query, request);
|
||||
let _ = self.sender.try_send(AnalyticsMsg::AggregateTasks(aggregate));
|
||||
}
|
||||
@ -868,21 +868,21 @@ pub struct TasksAggregator {
|
||||
}
|
||||
|
||||
impl TasksAggregator {
|
||||
pub fn from_query(query: &TasksFilterQueryRaw, request: &HttpRequest) -> Self {
|
||||
pub fn from_query(query: &TasksFilterQuery, request: &HttpRequest) -> Self {
|
||||
Self {
|
||||
timestamp: Some(OffsetDateTime::now_utc()),
|
||||
user_agents: extract_user_agents(request).into_iter().collect(),
|
||||
filtered_by_uid: query.common.uids.is_some(),
|
||||
filtered_by_index_uid: query.common.index_uids.is_some(),
|
||||
filtered_by_type: query.common.types.is_some(),
|
||||
filtered_by_status: query.common.statuses.is_some(),
|
||||
filtered_by_canceled_by: query.common.canceled_by.is_some(),
|
||||
filtered_by_before_enqueued_at: query.dates.before_enqueued_at.is_some(),
|
||||
filtered_by_after_enqueued_at: query.dates.after_enqueued_at.is_some(),
|
||||
filtered_by_before_started_at: query.dates.before_started_at.is_some(),
|
||||
filtered_by_after_started_at: query.dates.after_started_at.is_some(),
|
||||
filtered_by_before_finished_at: query.dates.before_finished_at.is_some(),
|
||||
filtered_by_after_finished_at: query.dates.after_finished_at.is_some(),
|
||||
filtered_by_uid: query.uids.is_some(),
|
||||
filtered_by_index_uid: query.index_uids.is_some(),
|
||||
filtered_by_type: query.types.is_some(),
|
||||
filtered_by_status: query.statuses.is_some(),
|
||||
filtered_by_canceled_by: query.canceled_by.is_some(),
|
||||
filtered_by_before_enqueued_at: query.before_enqueued_at.is_some(),
|
||||
filtered_by_after_enqueued_at: query.after_enqueued_at.is_some(),
|
||||
filtered_by_before_started_at: query.before_started_at.is_some(),
|
||||
filtered_by_after_started_at: query.after_started_at.is_some(),
|
||||
filtered_by_before_finished_at: query.before_finished_at.is_some(),
|
||||
filtered_by_after_finished_at: query.after_finished_at.is_some(),
|
||||
total_received: 1,
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ impl ErrorCode for MeilisearchHttpError {
|
||||
MeilisearchHttpError::MissingPayload(_) => Code::MissingPayload,
|
||||
MeilisearchHttpError::InvalidContentType(_, _) => Code::InvalidContentType,
|
||||
MeilisearchHttpError::DocumentNotFound(_) => Code::DocumentNotFound,
|
||||
MeilisearchHttpError::InvalidExpression(_, _) => Code::Filter,
|
||||
MeilisearchHttpError::InvalidExpression(_, _) => Code::InvalidSearchFilter,
|
||||
MeilisearchHttpError::PayloadTooLarge => Code::PayloadTooLarge,
|
||||
MeilisearchHttpError::SwapIndexPayloadWrongLength(_) => Code::InvalidSwapIndexes,
|
||||
MeilisearchHttpError::IndexUid(e) => e.error_code(),
|
||||
|
@ -17,7 +17,7 @@ impl ErrorCode for AuthenticationError {
|
||||
fn error_code(&self) -> Code {
|
||||
match self {
|
||||
AuthenticationError::MissingAuthorizationHeader => Code::MissingAuthorizationHeader,
|
||||
AuthenticationError::InvalidToken => Code::InvalidToken,
|
||||
AuthenticationError::InvalidToken => Code::InvalidApiKey,
|
||||
AuthenticationError::IrretrievableState => Code::Internal,
|
||||
AuthenticationError::MissingMasterKey => Code::MissingMasterKey,
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ impl<T, E> ValidatedJson<T, E> {
|
||||
|
||||
impl<T, E> FromRequest for ValidatedJson<T, E>
|
||||
where
|
||||
E: DeserializeError + ErrorCode + 'static,
|
||||
E: DeserializeError + ErrorCode + std::error::Error + 'static,
|
||||
T: DeserializeFromValue<E>,
|
||||
{
|
||||
type Error = actix_web::Error;
|
||||
@ -55,7 +55,7 @@ pub struct ValidatedJsonExtractFut<T, E> {
|
||||
impl<T, E> Future for ValidatedJsonExtractFut<T, E>
|
||||
where
|
||||
T: DeserializeFromValue<E>,
|
||||
E: DeserializeError + ErrorCode + 'static,
|
||||
E: DeserializeError + ErrorCode + std::error::Error + 'static,
|
||||
{
|
||||
type Output = Result<ValidatedJson<T, E>, actix_web::Error>;
|
||||
|
||||
|
@ -22,7 +22,7 @@ impl<T, E> QueryParameter<T, E> {
|
||||
impl<T, E> QueryParameter<T, E>
|
||||
where
|
||||
T: DeserializeFromValue<E>,
|
||||
E: DeserializeError + ErrorCode + 'static,
|
||||
E: DeserializeError + ErrorCode + std::error::Error + 'static,
|
||||
{
|
||||
pub fn from_query(query_str: &str) -> Result<Self, actix_web::Error> {
|
||||
let value = serde_urlencoded::from_str::<serde_json::Value>(query_str)
|
||||
@ -58,7 +58,7 @@ impl<T: fmt::Display, E> fmt::Display for QueryParameter<T, E> {
|
||||
impl<T, E> FromRequest for QueryParameter<T, E>
|
||||
where
|
||||
T: DeserializeFromValue<E>,
|
||||
E: DeserializeError + ErrorCode + 'static,
|
||||
E: DeserializeError + ErrorCode + std::error::Error + 'static,
|
||||
{
|
||||
type Error = actix_web::Error;
|
||||
type Future = Ready<Result<Self, actix_web::Error>>;
|
||||
|
@ -1,20 +1,21 @@
|
||||
use std::convert::Infallible;
|
||||
use std::num::ParseIntError;
|
||||
use std::{fmt, str};
|
||||
use std::str;
|
||||
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use deserr::{DeserializeError, IntoValue, MergeWithError, ValuePointerRef};
|
||||
use deserr::DeserializeFromValue;
|
||||
use meilisearch_auth::error::AuthControllerError;
|
||||
use meilisearch_auth::AuthController;
|
||||
use meilisearch_types::error::{unwrap_any, Code, ErrorCode, ResponseError};
|
||||
use meilisearch_types::keys::{Action, Key};
|
||||
use meilisearch_types::error::deserr_codes::*;
|
||||
use meilisearch_types::error::{Code, DeserrError, ResponseError, TakeErrorMessage};
|
||||
use meilisearch_types::keys::{Action, CreateApiKey, Key, PatchApiKey};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use time::OffsetDateTime;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::indexes::search::parse_usize_take_error_message;
|
||||
use super::PAGINATION_DEFAULT_LIMIT;
|
||||
use crate::extractors::authentication::policies::*;
|
||||
use crate::extractors::authentication::GuardedData;
|
||||
use crate::extractors::json::ValidatedJson;
|
||||
use crate::extractors::query_parameters::QueryParameter;
|
||||
use crate::extractors::sequential_extractor::SeqHandler;
|
||||
use crate::routes::Pagination;
|
||||
@ -35,7 +36,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
|
||||
pub async fn create_api_key(
|
||||
auth_controller: GuardedData<ActionPolicy<{ actions::KEYS_CREATE }>, AuthController>,
|
||||
body: web::Json<Value>,
|
||||
body: ValidatedJson<CreateApiKey, DeserrError>,
|
||||
_req: HttpRequest,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let v = body.into_inner();
|
||||
@ -49,72 +50,28 @@ pub async fn create_api_key(
|
||||
Ok(HttpResponse::Created().json(res))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PaginationDeserrError {
|
||||
error: String,
|
||||
code: Code,
|
||||
#[derive(DeserializeFromValue, Deserialize, Debug, Clone, Copy)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub struct ListApiKeys {
|
||||
#[serde(default)]
|
||||
#[deserr(error = DeserrError<InvalidApiKeyOffset>, default, from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
|
||||
pub offset: usize,
|
||||
#[serde(default = "PAGINATION_DEFAULT_LIMIT")]
|
||||
#[deserr(error = DeserrError<InvalidApiKeyLimit>, default = PAGINATION_DEFAULT_LIMIT(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
|
||||
pub limit: usize,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for PaginationDeserrError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for PaginationDeserrError {}
|
||||
impl ErrorCode for PaginationDeserrError {
|
||||
fn error_code(&self) -> Code {
|
||||
self.code
|
||||
}
|
||||
}
|
||||
|
||||
impl MergeWithError<PaginationDeserrError> for PaginationDeserrError {
|
||||
fn merge(
|
||||
_self_: Option<Self>,
|
||||
other: PaginationDeserrError,
|
||||
_merge_location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
Err(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl DeserializeError for PaginationDeserrError {
|
||||
fn error<V: IntoValue>(
|
||||
_self_: Option<Self>,
|
||||
error: deserr::ErrorKind<V>,
|
||||
location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
let error = unwrap_any(deserr::serde_json::JsonError::error(None, error, location)).0;
|
||||
|
||||
let code = match location.last_field() {
|
||||
Some("offset") => Code::InvalidApiKeyLimit,
|
||||
Some("limit") => Code::InvalidApiKeyOffset,
|
||||
_ => Code::BadRequest,
|
||||
};
|
||||
|
||||
Err(PaginationDeserrError { error, code })
|
||||
}
|
||||
}
|
||||
|
||||
impl MergeWithError<ParseIntError> for PaginationDeserrError {
|
||||
fn merge(
|
||||
_self_: Option<Self>,
|
||||
other: ParseIntError,
|
||||
merge_location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
PaginationDeserrError::error::<Infallible>(
|
||||
None,
|
||||
deserr::ErrorKind::Unexpected { msg: other.to_string() },
|
||||
merge_location,
|
||||
)
|
||||
impl ListApiKeys {
|
||||
fn as_pagination(self) -> Pagination {
|
||||
Pagination { offset: self.offset, limit: self.limit }
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_api_keys(
|
||||
auth_controller: GuardedData<ActionPolicy<{ actions::KEYS_GET }>, AuthController>,
|
||||
paginate: QueryParameter<Pagination, PaginationDeserrError>,
|
||||
list_api_keys: QueryParameter<ListApiKeys, DeserrError>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let paginate = paginate.into_inner();
|
||||
let paginate = list_api_keys.into_inner().as_pagination();
|
||||
let page_view = tokio::task::spawn_blocking(move || -> Result<_, AuthControllerError> {
|
||||
let keys = auth_controller.list_keys()?;
|
||||
let page_view = paginate
|
||||
@ -149,15 +106,15 @@ pub async fn get_api_key(
|
||||
|
||||
pub async fn patch_api_key(
|
||||
auth_controller: GuardedData<ActionPolicy<{ actions::KEYS_UPDATE }>, AuthController>,
|
||||
body: web::Json<Value>,
|
||||
body: ValidatedJson<PatchApiKey, DeserrError>,
|
||||
path: web::Path<AuthParam>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let key = path.into_inner().key;
|
||||
let body = body.into_inner();
|
||||
let patch_api_key = body.into_inner();
|
||||
let res = tokio::task::spawn_blocking(move || -> Result<_, AuthControllerError> {
|
||||
let uid =
|
||||
Uuid::parse_str(&key).or_else(|_| auth_controller.get_uid_from_encoded_key(&key))?;
|
||||
let key = auth_controller.update_key(uid, body)?;
|
||||
let key = auth_controller.update_key(uid, patch_api_key)?;
|
||||
|
||||
Ok(KeyView::from_key(key, &auth_controller))
|
||||
})
|
||||
|
@ -1,19 +1,17 @@
|
||||
use std::convert::Infallible;
|
||||
use std::fmt;
|
||||
use std::io::ErrorKind;
|
||||
use std::num::ParseIntError;
|
||||
use std::str::FromStr;
|
||||
|
||||
use actix_web::http::header::CONTENT_TYPE;
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{web, HttpMessage, HttpRequest, HttpResponse};
|
||||
use bstr::ByteSlice;
|
||||
use deserr::{DeserializeError, DeserializeFromValue, IntoValue, MergeWithError, ValuePointerRef};
|
||||
use deserr::DeserializeFromValue;
|
||||
use futures::StreamExt;
|
||||
use index_scheduler::IndexScheduler;
|
||||
use log::debug;
|
||||
use meilisearch_types::document_formats::{read_csv, read_json, read_ndjson, PayloadType};
|
||||
use meilisearch_types::error::{unwrap_any, Code, ErrorCode, ResponseError};
|
||||
use meilisearch_types::error::deserr_codes::*;
|
||||
use meilisearch_types::error::{DeserrError, ResponseError, TakeErrorMessage};
|
||||
use meilisearch_types::heed::RoTxn;
|
||||
use meilisearch_types::index_uid::IndexUid;
|
||||
use meilisearch_types::milli::update::IndexDocumentsMethod;
|
||||
@ -29,6 +27,7 @@ use tempfile::tempfile;
|
||||
use tokio::fs::File;
|
||||
use tokio::io::{AsyncSeekExt, AsyncWriteExt, BufWriter};
|
||||
|
||||
use super::search::parse_usize_take_error_message;
|
||||
use crate::analytics::{Analytics, DocumentDeletionKind};
|
||||
use crate::error::MeilisearchHttpError;
|
||||
use crate::error::PayloadError::ReceivePayload;
|
||||
@ -83,61 +82,16 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, DeserializeFromValue)]
|
||||
#[deserr(rename_all = camelCase, deny_unknown_fields)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
pub struct GetDocument {
|
||||
#[deserr(error = DeserrError<InvalidDocumentFields>)]
|
||||
fields: Option<CS<StarOr<String>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GetDocumentDeserrError {
|
||||
error: String,
|
||||
code: Code,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for GetDocumentDeserrError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for GetDocumentDeserrError {}
|
||||
impl ErrorCode for GetDocumentDeserrError {
|
||||
fn error_code(&self) -> Code {
|
||||
self.code
|
||||
}
|
||||
}
|
||||
|
||||
impl MergeWithError<GetDocumentDeserrError> for GetDocumentDeserrError {
|
||||
fn merge(
|
||||
_self_: Option<Self>,
|
||||
other: GetDocumentDeserrError,
|
||||
_merge_location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
Err(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl DeserializeError for GetDocumentDeserrError {
|
||||
fn error<V: IntoValue>(
|
||||
_self_: Option<Self>,
|
||||
error: deserr::ErrorKind<V>,
|
||||
location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
let error = unwrap_any(deserr::serde_json::JsonError::error(None, error, location)).0;
|
||||
|
||||
let code = match location.last_field() {
|
||||
Some("fields") => Code::InvalidDocumentFields,
|
||||
_ => Code::BadRequest,
|
||||
};
|
||||
|
||||
Err(GetDocumentDeserrError { error, code })
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_document(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_GET }>, Data<IndexScheduler>>,
|
||||
path: web::Path<DocumentParam>,
|
||||
params: QueryParameter<GetDocument, GetDocumentDeserrError>,
|
||||
params: QueryParameter<GetDocument, DeserrError>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let GetDocument { fields } = params.into_inner();
|
||||
let attributes_to_retrieve = fields.and_then(fold_star_or);
|
||||
@ -165,81 +119,20 @@ pub async fn delete_document(
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, DeserializeFromValue)]
|
||||
#[deserr(rename_all = camelCase, deny_unknown_fields)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
pub struct BrowseQuery {
|
||||
#[deserr(default, from(&String) = FromStr::from_str -> ParseIntError)]
|
||||
#[deserr(error = DeserrError<InvalidDocumentFields>, default, from(&String) = parse_usize_take_error_message -> TakeErrorMessage<ParseIntError>)]
|
||||
offset: usize,
|
||||
#[deserr(default = crate::routes::PAGINATION_DEFAULT_LIMIT(), from(&String) = FromStr::from_str -> ParseIntError)]
|
||||
#[deserr(error = DeserrError<InvalidDocumentLimit>, default = crate::routes::PAGINATION_DEFAULT_LIMIT(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<ParseIntError>)]
|
||||
limit: usize,
|
||||
#[deserr(error = DeserrError<InvalidDocumentLimit>)]
|
||||
fields: Option<CS<StarOr<String>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BrowseQueryDeserrError {
|
||||
error: String,
|
||||
code: Code,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for BrowseQueryDeserrError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for BrowseQueryDeserrError {}
|
||||
impl ErrorCode for BrowseQueryDeserrError {
|
||||
fn error_code(&self) -> Code {
|
||||
self.code
|
||||
}
|
||||
}
|
||||
|
||||
impl MergeWithError<BrowseQueryDeserrError> for BrowseQueryDeserrError {
|
||||
fn merge(
|
||||
_self_: Option<Self>,
|
||||
other: BrowseQueryDeserrError,
|
||||
_merge_location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
Err(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl DeserializeError for BrowseQueryDeserrError {
|
||||
fn error<V: IntoValue>(
|
||||
_self_: Option<Self>,
|
||||
error: deserr::ErrorKind<V>,
|
||||
location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
let error = unwrap_any(deserr::serde_json::JsonError::error(None, error, location)).0;
|
||||
|
||||
let code = match location.last_field() {
|
||||
Some("fields") => Code::InvalidDocumentFields,
|
||||
Some("offset") => Code::InvalidDocumentOffset,
|
||||
Some("limit") => Code::InvalidDocumentLimit,
|
||||
_ => Code::BadRequest,
|
||||
};
|
||||
|
||||
Err(BrowseQueryDeserrError { error, code })
|
||||
}
|
||||
}
|
||||
|
||||
impl MergeWithError<ParseIntError> for BrowseQueryDeserrError {
|
||||
fn merge(
|
||||
_self_: Option<Self>,
|
||||
other: ParseIntError,
|
||||
merge_location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
BrowseQueryDeserrError::error::<Infallible>(
|
||||
None,
|
||||
deserr::ErrorKind::Unexpected { msg: other.to_string() },
|
||||
merge_location,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_all_documents(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_GET }>, Data<IndexScheduler>>,
|
||||
index_uid: web::Path<String>,
|
||||
params: QueryParameter<BrowseQuery, BrowseQueryDeserrError>,
|
||||
params: QueryParameter<BrowseQuery, DeserrError>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
debug!("called with params: {:?}", params);
|
||||
let BrowseQuery { limit, offset, fields } = params.into_inner();
|
||||
@ -255,61 +148,16 @@ pub async fn get_all_documents(
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, DeserializeFromValue)]
|
||||
#[deserr(rename_all = camelCase, deny_unknown_fields)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
pub struct UpdateDocumentsQuery {
|
||||
#[deserr(error = DeserrError<InvalidIndexPrimaryKey>)]
|
||||
pub primary_key: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UpdateDocumentsQueryDeserrError {
|
||||
error: String,
|
||||
code: Code,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for UpdateDocumentsQueryDeserrError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for UpdateDocumentsQueryDeserrError {}
|
||||
impl ErrorCode for UpdateDocumentsQueryDeserrError {
|
||||
fn error_code(&self) -> Code {
|
||||
self.code
|
||||
}
|
||||
}
|
||||
|
||||
impl MergeWithError<UpdateDocumentsQueryDeserrError> for UpdateDocumentsQueryDeserrError {
|
||||
fn merge(
|
||||
_self_: Option<Self>,
|
||||
other: UpdateDocumentsQueryDeserrError,
|
||||
_merge_location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
Err(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl DeserializeError for UpdateDocumentsQueryDeserrError {
|
||||
fn error<V: IntoValue>(
|
||||
_self_: Option<Self>,
|
||||
error: deserr::ErrorKind<V>,
|
||||
location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
let error = unwrap_any(deserr::serde_json::JsonError::error(None, error, location)).0;
|
||||
|
||||
let code = match location.last_field() {
|
||||
Some("primaryKey") => Code::InvalidIndexPrimaryKey,
|
||||
_ => Code::BadRequest,
|
||||
};
|
||||
|
||||
Err(UpdateDocumentsQueryDeserrError { error, code })
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn add_documents(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_ADD }>, Data<IndexScheduler>>,
|
||||
index_uid: web::Path<String>,
|
||||
params: QueryParameter<UpdateDocumentsQuery, UpdateDocumentsQueryDeserrError>,
|
||||
params: QueryParameter<UpdateDocumentsQuery, DeserrError>,
|
||||
body: Payload,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
@ -337,7 +185,7 @@ pub async fn add_documents(
|
||||
pub async fn update_documents(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_ADD }>, Data<IndexScheduler>>,
|
||||
path: web::Path<String>,
|
||||
params: QueryParameter<UpdateDocumentsQuery, UpdateDocumentsQueryDeserrError>,
|
||||
params: QueryParameter<UpdateDocumentsQuery, DeserrError>,
|
||||
body: Payload,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
|
@ -1,14 +1,12 @@
|
||||
use std::convert::Infallible;
|
||||
use std::num::ParseIntError;
|
||||
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use deserr::{
|
||||
DeserializeError, DeserializeFromValue, ErrorKind, IntoValue, MergeWithError, ValuePointerRef,
|
||||
};
|
||||
use deserr::{DeserializeError, DeserializeFromValue, ValuePointerRef};
|
||||
use index_scheduler::IndexScheduler;
|
||||
use log::debug;
|
||||
use meilisearch_types::error::{unwrap_any, Code, ErrorCode, ResponseError};
|
||||
use meilisearch_types::error::deserr_codes::*;
|
||||
use meilisearch_types::error::{unwrap_any, Code, DeserrError, ResponseError, TakeErrorMessage};
|
||||
use meilisearch_types::index_uid::IndexUid;
|
||||
use meilisearch_types::milli::{self, FieldDistribution, Index};
|
||||
use meilisearch_types::tasks::KindWithContent;
|
||||
@ -16,7 +14,8 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use super::{Pagination, SummarizedTaskView};
|
||||
use self::search::parse_usize_take_error_message;
|
||||
use super::{Pagination, SummarizedTaskView, PAGINATION_DEFAULT_LIMIT};
|
||||
use crate::analytics::Analytics;
|
||||
use crate::extractors::authentication::policies::*;
|
||||
use crate::extractors::authentication::{AuthenticationError, GuardedData};
|
||||
@ -72,9 +71,26 @@ impl IndexView {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeserializeFromValue, Deserialize, Debug, Clone, Copy)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub struct ListIndexes {
|
||||
#[serde(default)]
|
||||
#[deserr(error = DeserrError<InvalidIndexOffset>, default, from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
|
||||
pub offset: usize,
|
||||
#[serde(default = "PAGINATION_DEFAULT_LIMIT")]
|
||||
#[deserr(error = DeserrError<InvalidIndexLimit>, default = PAGINATION_DEFAULT_LIMIT(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
|
||||
pub limit: usize,
|
||||
}
|
||||
impl ListIndexes {
|
||||
fn as_pagination(self) -> Pagination {
|
||||
Pagination { offset: self.offset, limit: self.limit }
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_indexes(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_GET }>, Data<IndexScheduler>>,
|
||||
paginate: QueryParameter<Pagination, ListIndexesDeserrError>,
|
||||
paginate: QueryParameter<ListIndexes, DeserrError>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let search_rules = &index_scheduler.filters().search_rules;
|
||||
let indexes: Vec<_> = index_scheduler.indexes()?;
|
||||
@ -84,82 +100,24 @@ pub async fn list_indexes(
|
||||
.map(|(name, index)| IndexView::new(name, &index))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let ret = paginate.auto_paginate_sized(indexes.into_iter());
|
||||
let ret = paginate.as_pagination().auto_paginate_sized(indexes.into_iter());
|
||||
|
||||
debug!("returns: {:?}", ret);
|
||||
Ok(HttpResponse::Ok().json(ret))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ListIndexesDeserrError {
|
||||
error: String,
|
||||
code: Code,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ListIndexesDeserrError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ListIndexesDeserrError {}
|
||||
impl ErrorCode for ListIndexesDeserrError {
|
||||
fn error_code(&self) -> Code {
|
||||
self.code
|
||||
}
|
||||
}
|
||||
|
||||
impl MergeWithError<ListIndexesDeserrError> for ListIndexesDeserrError {
|
||||
fn merge(
|
||||
_self_: Option<Self>,
|
||||
other: ListIndexesDeserrError,
|
||||
_merge_location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
Err(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl deserr::DeserializeError for ListIndexesDeserrError {
|
||||
fn error<V: IntoValue>(
|
||||
_self_: Option<Self>,
|
||||
error: ErrorKind<V>,
|
||||
location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
let code = match location.last_field() {
|
||||
Some("offset") => Code::InvalidIndexLimit,
|
||||
Some("limit") => Code::InvalidIndexOffset,
|
||||
_ => Code::BadRequest,
|
||||
};
|
||||
let error = unwrap_any(deserr::serde_json::JsonError::error(None, error, location)).0;
|
||||
|
||||
Err(ListIndexesDeserrError { error, code })
|
||||
}
|
||||
}
|
||||
|
||||
impl MergeWithError<ParseIntError> for ListIndexesDeserrError {
|
||||
fn merge(
|
||||
_self_: Option<Self>,
|
||||
other: ParseIntError,
|
||||
merge_location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
ListIndexesDeserrError::error::<Infallible>(
|
||||
None,
|
||||
ErrorKind::Unexpected { msg: other.to_string() },
|
||||
merge_location,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeserializeFromValue, Debug)]
|
||||
#[deserr(rename_all = camelCase, deny_unknown_fields)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
pub struct IndexCreateRequest {
|
||||
#[deserr(error = DeserrError<InvalidIndexUid>, missing_field_error = DeserrError::missing_index_uid)]
|
||||
uid: String,
|
||||
#[deserr(error = DeserrError<InvalidIndexPrimaryKey>)]
|
||||
primary_key: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn create_index(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_CREATE }>, Data<IndexScheduler>>,
|
||||
body: ValidatedJson<IndexCreateRequest, CreateIndexesDeserrError>,
|
||||
body: ValidatedJson<IndexCreateRequest, DeserrError>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
@ -184,58 +142,29 @@ pub async fn create_index(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CreateIndexesDeserrError {
|
||||
error: String,
|
||||
code: Code,
|
||||
fn deny_immutable_fields_index(
|
||||
field: &str,
|
||||
accepted: &[&str],
|
||||
location: ValuePointerRef,
|
||||
) -> DeserrError {
|
||||
let mut error = unwrap_any(DeserrError::<BadRequest>::error::<Infallible>(
|
||||
None,
|
||||
deserr::ErrorKind::UnknownKey { key: field, accepted },
|
||||
location,
|
||||
));
|
||||
|
||||
error.code = match field {
|
||||
"uid" => Code::ImmutableIndexUid,
|
||||
"createdAt" => Code::ImmutableIndexCreatedAt,
|
||||
"updatedAt" => Code::ImmutableIndexUpdatedAt,
|
||||
_ => Code::BadRequest,
|
||||
};
|
||||
error
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CreateIndexesDeserrError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CreateIndexesDeserrError {}
|
||||
impl ErrorCode for CreateIndexesDeserrError {
|
||||
fn error_code(&self) -> Code {
|
||||
self.code
|
||||
}
|
||||
}
|
||||
|
||||
impl MergeWithError<CreateIndexesDeserrError> for CreateIndexesDeserrError {
|
||||
fn merge(
|
||||
_self_: Option<Self>,
|
||||
other: CreateIndexesDeserrError,
|
||||
_merge_location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
Err(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl deserr::DeserializeError for CreateIndexesDeserrError {
|
||||
fn error<V: IntoValue>(
|
||||
_self_: Option<Self>,
|
||||
error: ErrorKind<V>,
|
||||
location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
let code = match location.last_field() {
|
||||
Some("uid") => Code::InvalidIndexUid,
|
||||
Some("primaryKey") => Code::InvalidIndexPrimaryKey,
|
||||
None if matches!(error, ErrorKind::MissingField { field } if field == "uid") => {
|
||||
Code::MissingIndexUid
|
||||
}
|
||||
_ => Code::BadRequest,
|
||||
};
|
||||
let error = unwrap_any(deserr::serde_json::JsonError::error(None, error, location)).0;
|
||||
|
||||
Err(CreateIndexesDeserrError { error, code })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeserializeFromValue, Debug)]
|
||||
#[deserr(rename_all = camelCase, deny_unknown_fields)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields = deny_immutable_fields_index)]
|
||||
pub struct UpdateIndexRequest {
|
||||
#[deserr(error = DeserrError<InvalidIndexPrimaryKey>)]
|
||||
primary_key: Option<String>,
|
||||
}
|
||||
|
||||
@ -254,7 +183,7 @@ pub async fn get_index(
|
||||
pub async fn update_index(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_UPDATE }>, Data<IndexScheduler>>,
|
||||
path: web::Path<String>,
|
||||
body: ValidatedJson<UpdateIndexRequest, UpdateIndexesDeserrError>,
|
||||
body: ValidatedJson<UpdateIndexRequest, DeserrError>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
@ -278,51 +207,6 @@ pub async fn update_index(
|
||||
Ok(HttpResponse::Accepted().json(task))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UpdateIndexesDeserrError {
|
||||
error: String,
|
||||
code: Code,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for UpdateIndexesDeserrError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for UpdateIndexesDeserrError {}
|
||||
impl ErrorCode for UpdateIndexesDeserrError {
|
||||
fn error_code(&self) -> Code {
|
||||
self.code
|
||||
}
|
||||
}
|
||||
|
||||
impl MergeWithError<UpdateIndexesDeserrError> for UpdateIndexesDeserrError {
|
||||
fn merge(
|
||||
_self_: Option<Self>,
|
||||
other: UpdateIndexesDeserrError,
|
||||
_merge_location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
Err(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl deserr::DeserializeError for UpdateIndexesDeserrError {
|
||||
fn error<V: IntoValue>(
|
||||
_self_: Option<Self>,
|
||||
error: ErrorKind<V>,
|
||||
location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
let code = match location.last_field() {
|
||||
Some("primaryKey") => Code::InvalidIndexPrimaryKey,
|
||||
_ => Code::BadRequest,
|
||||
};
|
||||
let error = unwrap_any(deserr::serde_json::JsonError::error(None, error, location)).0;
|
||||
|
||||
Err(UpdateIndexesDeserrError { error, code })
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete_index(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_DELETE }>, Data<IndexScheduler>>,
|
||||
index_uid: web::Path<String>,
|
||||
|
@ -5,7 +5,8 @@ use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use index_scheduler::IndexScheduler;
|
||||
use log::debug;
|
||||
use meilisearch_auth::IndexSearchRules;
|
||||
use meilisearch_types::error::ResponseError;
|
||||
use meilisearch_types::error::deserr_codes::*;
|
||||
use meilisearch_types::error::{DeserrError, ResponseError, TakeErrorMessage};
|
||||
use serde_cs::vec::CS;
|
||||
use serde_json::Value;
|
||||
|
||||
@ -15,11 +16,11 @@ use crate::extractors::authentication::GuardedData;
|
||||
use crate::extractors::json::ValidatedJson;
|
||||
use crate::extractors::query_parameters::QueryParameter;
|
||||
use crate::extractors::sequential_extractor::SeqHandler;
|
||||
use crate::routes::from_string_to_option;
|
||||
use crate::routes::from_string_to_option_take_error_message;
|
||||
use crate::search::{
|
||||
perform_search, MatchingStrategy, SearchDeserError, SearchQuery, DEFAULT_CROP_LENGTH,
|
||||
DEFAULT_CROP_MARKER, DEFAULT_HIGHLIGHT_POST_TAG, DEFAULT_HIGHLIGHT_PRE_TAG,
|
||||
DEFAULT_SEARCH_LIMIT, DEFAULT_SEARCH_OFFSET,
|
||||
perform_search, MatchingStrategy, SearchQuery, DEFAULT_CROP_LENGTH, DEFAULT_CROP_MARKER,
|
||||
DEFAULT_HIGHLIGHT_POST_TAG, DEFAULT_HIGHLIGHT_PRE_TAG, DEFAULT_SEARCH_LIMIT,
|
||||
DEFAULT_SEARCH_OFFSET,
|
||||
};
|
||||
|
||||
pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
@ -30,35 +31,54 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn parse_usize_take_error_message(
|
||||
s: &str,
|
||||
) -> Result<usize, TakeErrorMessage<std::num::ParseIntError>> {
|
||||
usize::from_str(s).map_err(TakeErrorMessage)
|
||||
}
|
||||
|
||||
pub fn parse_bool_take_error_message(
|
||||
s: &str,
|
||||
) -> Result<bool, TakeErrorMessage<std::str::ParseBoolError>> {
|
||||
s.parse().map_err(TakeErrorMessage)
|
||||
}
|
||||
|
||||
#[derive(Debug, deserr::DeserializeFromValue)]
|
||||
#[deserr(rename_all = camelCase, deny_unknown_fields)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
pub struct SearchQueryGet {
|
||||
#[deserr(error = DeserrError<InvalidSearchQ>)]
|
||||
q: Option<String>,
|
||||
#[deserr(default = DEFAULT_SEARCH_OFFSET(), from(&String) = FromStr::from_str -> std::num::ParseIntError)]
|
||||
#[deserr(error = DeserrError<InvalidSearchOffset>, default = DEFAULT_SEARCH_OFFSET(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
|
||||
offset: usize,
|
||||
#[deserr(default = DEFAULT_SEARCH_LIMIT(), from(&String) = FromStr::from_str -> std::num::ParseIntError)]
|
||||
#[deserr(error = DeserrError<InvalidSearchLimit>, default = DEFAULT_SEARCH_LIMIT(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
|
||||
limit: usize,
|
||||
#[deserr(from(&String) = from_string_to_option -> std::num::ParseIntError)]
|
||||
#[deserr(error = DeserrError<InvalidSearchPage>, from(&String) = from_string_to_option_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
|
||||
page: Option<usize>,
|
||||
#[deserr(from(&String) = from_string_to_option -> std::num::ParseIntError)]
|
||||
#[deserr(error = DeserrError<InvalidSearchHitsPerPage>, from(&String) = from_string_to_option_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
|
||||
hits_per_page: Option<usize>,
|
||||
#[deserr(error = DeserrError<InvalidSearchAttributesToRetrieve>)]
|
||||
attributes_to_retrieve: Option<CS<String>>,
|
||||
#[deserr(error = DeserrError<InvalidSearchAttributesToCrop>)]
|
||||
attributes_to_crop: Option<CS<String>>,
|
||||
#[deserr(default = DEFAULT_CROP_LENGTH(), from(&String) = FromStr::from_str -> std::num::ParseIntError)]
|
||||
#[deserr(error = DeserrError<InvalidSearchCropLength>, default = DEFAULT_CROP_LENGTH(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
|
||||
crop_length: usize,
|
||||
#[deserr(error = DeserrError<InvalidSearchAttributesToHighlight>)]
|
||||
attributes_to_highlight: Option<CS<String>>,
|
||||
#[deserr(error = DeserrError<InvalidSearchFilter>)]
|
||||
filter: Option<String>,
|
||||
#[deserr(error = DeserrError<InvalidSearchSort>)]
|
||||
sort: Option<String>,
|
||||
#[deserr(default, from(&String) = FromStr::from_str -> std::str::ParseBoolError)]
|
||||
#[deserr(error = DeserrError<InvalidSearchShowMatchesPosition>, default, from(&String) = parse_bool_take_error_message -> TakeErrorMessage<std::str::ParseBoolError>)]
|
||||
show_matches_position: bool,
|
||||
#[deserr(error = DeserrError<InvalidSearchFacets>)]
|
||||
facets: Option<CS<String>>,
|
||||
#[deserr(default = DEFAULT_HIGHLIGHT_PRE_TAG())]
|
||||
#[deserr(error = DeserrError<InvalidSearchHighlightPreTag>, default = DEFAULT_HIGHLIGHT_PRE_TAG())]
|
||||
highlight_pre_tag: String,
|
||||
#[deserr(default = DEFAULT_HIGHLIGHT_POST_TAG())]
|
||||
#[deserr(error = DeserrError<InvalidSearchHighlightPostTag>, default = DEFAULT_HIGHLIGHT_POST_TAG())]
|
||||
highlight_post_tag: String,
|
||||
#[deserr(default = DEFAULT_CROP_MARKER())]
|
||||
#[deserr(error = DeserrError<InvalidSearchCropMarker>, default = DEFAULT_CROP_MARKER())]
|
||||
crop_marker: String,
|
||||
#[deserr(default)]
|
||||
#[deserr(error = DeserrError<InvalidSearchMatchingStrategy>, default)]
|
||||
matching_strategy: MatchingStrategy,
|
||||
}
|
||||
|
||||
@ -142,7 +162,7 @@ fn fix_sort_query_parameters(sort_query: &str) -> Vec<String> {
|
||||
pub async fn search_with_url_query(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::SEARCH }>, Data<IndexScheduler>>,
|
||||
index_uid: web::Path<String>,
|
||||
params: QueryParameter<SearchQueryGet, SearchDeserError>,
|
||||
params: QueryParameter<SearchQueryGet, DeserrError>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
@ -174,7 +194,7 @@ pub async fn search_with_url_query(
|
||||
pub async fn search_with_post(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::SEARCH }>, Data<IndexScheduler>>,
|
||||
index_uid: web::Path<String>,
|
||||
params: ValidatedJson<SearchQuery, SearchDeserError>,
|
||||
params: ValidatedJson<SearchQuery, DeserrError>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
|
@ -1,13 +1,10 @@
|
||||
use std::fmt;
|
||||
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use deserr::{IntoValue, ValuePointerRef};
|
||||
use index_scheduler::IndexScheduler;
|
||||
use log::debug;
|
||||
use meilisearch_types::error::{unwrap_any, Code, ErrorCode, ResponseError};
|
||||
use meilisearch_types::error::{DeserrError, ResponseError};
|
||||
use meilisearch_types::index_uid::IndexUid;
|
||||
use meilisearch_types::settings::{settings, Settings, Unchecked};
|
||||
use meilisearch_types::settings::{settings, RankingRuleView, Settings, Unchecked};
|
||||
use meilisearch_types::tasks::KindWithContent;
|
||||
use serde_json::json;
|
||||
|
||||
@ -19,7 +16,7 @@ use crate::routes::SummarizedTaskView;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! make_setting_route {
|
||||
($route:literal, $update_verb:ident, $type:ty, $attr:ident, $camelcase_attr:literal, $analytics_var:ident, $analytics:expr) => {
|
||||
($route:literal, $update_verb:ident, $type:ty, $err_ty:ty, $attr:ident, $camelcase_attr:literal, $analytics_var:ident, $analytics:expr) => {
|
||||
pub mod $attr {
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{web, HttpRequest, HttpResponse, Resource};
|
||||
@ -68,7 +65,7 @@ macro_rules! make_setting_route {
|
||||
Data<IndexScheduler>,
|
||||
>,
|
||||
index_uid: actix_web::web::Path<String>,
|
||||
body: actix_web::web::Json<Option<$type>>,
|
||||
body: $crate::routes::indexes::ValidatedJson<Option<$type>, $err_ty>,
|
||||
req: HttpRequest,
|
||||
$analytics_var: web::Data<dyn Analytics>,
|
||||
) -> std::result::Result<HttpResponse, ResponseError> {
|
||||
@ -133,6 +130,9 @@ make_setting_route!(
|
||||
"/filterable-attributes",
|
||||
put,
|
||||
std::collections::BTreeSet<String>,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsFilterableAttributes,
|
||||
>,
|
||||
filterable_attributes,
|
||||
"filterableAttributes",
|
||||
analytics,
|
||||
@ -156,6 +156,9 @@ make_setting_route!(
|
||||
"/sortable-attributes",
|
||||
put,
|
||||
std::collections::BTreeSet<String>,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsSortableAttributes,
|
||||
>,
|
||||
sortable_attributes,
|
||||
"sortableAttributes",
|
||||
analytics,
|
||||
@ -179,6 +182,9 @@ make_setting_route!(
|
||||
"/displayed-attributes",
|
||||
put,
|
||||
Vec<String>,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsDisplayedAttributes,
|
||||
>,
|
||||
displayed_attributes,
|
||||
"displayedAttributes",
|
||||
analytics,
|
||||
@ -202,6 +208,9 @@ make_setting_route!(
|
||||
"/typo-tolerance",
|
||||
patch,
|
||||
meilisearch_types::settings::TypoSettings,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsTypoTolerance,
|
||||
>,
|
||||
typo_tolerance,
|
||||
"typoTolerance",
|
||||
analytics,
|
||||
@ -212,7 +221,7 @@ make_setting_route!(
|
||||
"TypoTolerance Updated".to_string(),
|
||||
json!({
|
||||
"typo_tolerance": {
|
||||
"enabled": setting.as_ref().map(|s| !matches!(s.enabled.into(), Setting::Set(false))),
|
||||
"enabled": setting.as_ref().map(|s| !matches!(s.enabled, Setting::Set(false))),
|
||||
"disable_on_attributes": setting
|
||||
.as_ref()
|
||||
.and_then(|s| s.disable_on_attributes.as_ref().set().map(|m| !m.is_empty())),
|
||||
@ -244,6 +253,9 @@ make_setting_route!(
|
||||
"/searchable-attributes",
|
||||
put,
|
||||
Vec<String>,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsSearchableAttributes,
|
||||
>,
|
||||
searchable_attributes,
|
||||
"searchableAttributes",
|
||||
analytics,
|
||||
@ -267,6 +279,9 @@ make_setting_route!(
|
||||
"/stop-words",
|
||||
put,
|
||||
std::collections::BTreeSet<String>,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsStopWords,
|
||||
>,
|
||||
stop_words,
|
||||
"stopWords",
|
||||
analytics,
|
||||
@ -289,6 +304,9 @@ make_setting_route!(
|
||||
"/synonyms",
|
||||
put,
|
||||
std::collections::BTreeMap<String, Vec<String>>,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsSynonyms,
|
||||
>,
|
||||
synonyms,
|
||||
"synonyms",
|
||||
analytics,
|
||||
@ -311,6 +329,9 @@ make_setting_route!(
|
||||
"/distinct-attribute",
|
||||
put,
|
||||
String,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsDistinctAttribute,
|
||||
>,
|
||||
distinct_attribute,
|
||||
"distinctAttribute",
|
||||
analytics,
|
||||
@ -331,24 +352,27 @@ make_setting_route!(
|
||||
make_setting_route!(
|
||||
"/ranking-rules",
|
||||
put,
|
||||
Vec<String>,
|
||||
Vec<meilisearch_types::settings::RankingRuleView>,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsRankingRules,
|
||||
>,
|
||||
ranking_rules,
|
||||
"rankingRules",
|
||||
analytics,
|
||||
|setting: &Option<Vec<String>>, req: &HttpRequest| {
|
||||
|setting: &Option<Vec<meilisearch_types::settings::RankingRuleView>>, req: &HttpRequest| {
|
||||
use serde_json::json;
|
||||
|
||||
analytics.publish(
|
||||
"RankingRules Updated".to_string(),
|
||||
json!({
|
||||
"ranking_rules": {
|
||||
"words_position": setting.as_ref().map(|rr| rr.iter().position(|s| s == "words")),
|
||||
"typo_position": setting.as_ref().map(|rr| rr.iter().position(|s| s == "typo")),
|
||||
"proximity_position": setting.as_ref().map(|rr| rr.iter().position(|s| s == "proximity")),
|
||||
"attribute_position": setting.as_ref().map(|rr| rr.iter().position(|s| s == "attribute")),
|
||||
"sort_position": setting.as_ref().map(|rr| rr.iter().position(|s| s == "sort")),
|
||||
"exactness_position": setting.as_ref().map(|rr| rr.iter().position(|s| s == "exactness")),
|
||||
"values": setting.as_ref().map(|rr| rr.iter().filter(|s| !s.contains(':')).cloned().collect::<Vec<_>>().join(", ")),
|
||||
"words_position": setting.as_ref().map(|rr| rr.iter().position(|s| matches!(s, meilisearch_types::settings::RankingRuleView::Words))),
|
||||
"typo_position": setting.as_ref().map(|rr| rr.iter().position(|s| matches!(s, meilisearch_types::settings::RankingRuleView::Typo))),
|
||||
"proximity_position": setting.as_ref().map(|rr| rr.iter().position(|s| matches!(s, meilisearch_types::settings::RankingRuleView::Proximity))),
|
||||
"attribute_position": setting.as_ref().map(|rr| rr.iter().position(|s| matches!(s, meilisearch_types::settings::RankingRuleView::Attribute))),
|
||||
"sort_position": setting.as_ref().map(|rr| rr.iter().position(|s| matches!(s, meilisearch_types::settings::RankingRuleView::Sort))),
|
||||
"exactness_position": setting.as_ref().map(|rr| rr.iter().position(|s| matches!(s, meilisearch_types::settings::RankingRuleView::Exactness))),
|
||||
"values": setting.as_ref().map(|rr| rr.iter().filter(|s| matches!(s, meilisearch_types::settings::RankingRuleView::Asc(_) | meilisearch_types::settings::RankingRuleView::Desc(_)) ).map(|x| x.to_string()).collect::<Vec<_>>().join(", ")),
|
||||
}
|
||||
}),
|
||||
Some(req),
|
||||
@ -360,6 +384,9 @@ make_setting_route!(
|
||||
"/faceting",
|
||||
patch,
|
||||
meilisearch_types::settings::FacetingSettings,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsFaceting,
|
||||
>,
|
||||
faceting,
|
||||
"faceting",
|
||||
analytics,
|
||||
@ -382,6 +409,9 @@ make_setting_route!(
|
||||
"/pagination",
|
||||
patch,
|
||||
meilisearch_types::settings::PaginationSettings,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsPagination,
|
||||
>,
|
||||
pagination,
|
||||
"pagination",
|
||||
analytics,
|
||||
@ -428,66 +458,10 @@ generate_configure!(
|
||||
faceting
|
||||
);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SettingsDeserrError {
|
||||
error: String,
|
||||
code: Code,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SettingsDeserrError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for SettingsDeserrError {}
|
||||
impl ErrorCode for SettingsDeserrError {
|
||||
fn error_code(&self) -> Code {
|
||||
self.code
|
||||
}
|
||||
}
|
||||
|
||||
impl deserr::MergeWithError<SettingsDeserrError> for SettingsDeserrError {
|
||||
fn merge(
|
||||
_self_: Option<Self>,
|
||||
other: SettingsDeserrError,
|
||||
_merge_location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
Err(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl deserr::DeserializeError for SettingsDeserrError {
|
||||
fn error<V: IntoValue>(
|
||||
_self_: Option<Self>,
|
||||
error: deserr::ErrorKind<V>,
|
||||
location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
let error = unwrap_any(deserr::serde_json::JsonError::error(None, error, location)).0;
|
||||
|
||||
let code = match location.first_field() {
|
||||
Some("displayedAttributes") => Code::InvalidSettingsDisplayedAttributes,
|
||||
Some("searchableAttributes") => Code::InvalidSettingsSearchableAttributes,
|
||||
Some("filterableAttributes") => Code::InvalidSettingsFilterableAttributes,
|
||||
Some("sortableAttributes") => Code::InvalidSettingsSortableAttributes,
|
||||
Some("rankingRules") => Code::InvalidSettingsRankingRules,
|
||||
Some("stopWords") => Code::InvalidSettingsStopWords,
|
||||
Some("synonyms") => Code::InvalidSettingsSynonyms,
|
||||
Some("distinctAttribute") => Code::InvalidSettingsDistinctAttribute,
|
||||
Some("typoTolerance") => Code::InvalidSettingsTypoTolerance,
|
||||
Some("faceting") => Code::InvalidSettingsFaceting,
|
||||
Some("pagination") => Code::InvalidSettingsPagination,
|
||||
_ => Code::BadRequest,
|
||||
};
|
||||
|
||||
Err(SettingsDeserrError { error, code })
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_all(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::SETTINGS_UPDATE }>, Data<IndexScheduler>>,
|
||||
index_uid: web::Path<String>,
|
||||
body: ValidatedJson<Settings<Unchecked>, SettingsDeserrError>,
|
||||
body: ValidatedJson<Settings<Unchecked>, DeserrError>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
@ -497,13 +471,13 @@ pub async fn update_all(
|
||||
"Settings Updated".to_string(),
|
||||
json!({
|
||||
"ranking_rules": {
|
||||
"words_position": new_settings.ranking_rules.as_ref().set().map(|rr| rr.iter().position(|s| s == "words")),
|
||||
"typo_position": new_settings.ranking_rules.as_ref().set().map(|rr| rr.iter().position(|s| s == "typo")),
|
||||
"proximity_position": new_settings.ranking_rules.as_ref().set().map(|rr| rr.iter().position(|s| s == "proximity")),
|
||||
"attribute_position": new_settings.ranking_rules.as_ref().set().map(|rr| rr.iter().position(|s| s == "attribute")),
|
||||
"sort_position": new_settings.ranking_rules.as_ref().set().map(|rr| rr.iter().position(|s| s == "sort")),
|
||||
"exactness_position": new_settings.ranking_rules.as_ref().set().map(|rr| rr.iter().position(|s| s == "exactness")),
|
||||
"values": new_settings.ranking_rules.as_ref().set().map(|rr| rr.iter().filter(|s| !s.contains(':')).cloned().collect::<Vec<_>>().join(", ")),
|
||||
"words_position": new_settings.ranking_rules.as_ref().set().map(|rr| rr.iter().position(|s| matches!(s, RankingRuleView::Words))),
|
||||
"typo_position": new_settings.ranking_rules.as_ref().set().map(|rr| rr.iter().position(|s| matches!(s, RankingRuleView::Typo))),
|
||||
"proximity_position": new_settings.ranking_rules.as_ref().set().map(|rr| rr.iter().position(|s| matches!(s, RankingRuleView::Proximity))),
|
||||
"attribute_position": new_settings.ranking_rules.as_ref().set().map(|rr| rr.iter().position(|s| matches!(s, RankingRuleView::Attribute))),
|
||||
"sort_position": new_settings.ranking_rules.as_ref().set().map(|rr| rr.iter().position(|s| matches!(s, RankingRuleView::Sort))),
|
||||
"exactness_position": new_settings.ranking_rules.as_ref().set().map(|rr| rr.iter().position(|s| matches!(s, RankingRuleView::Exactness))),
|
||||
"values": new_settings.ranking_rules.as_ref().set().map(|rr| rr.iter().filter(|s| !matches!(s, RankingRuleView::Asc(_) | RankingRuleView::Desc(_)) ).map(|x| x.to_string()).collect::<Vec<_>>().join(", ")),
|
||||
},
|
||||
"searchable_attributes": {
|
||||
"total": new_settings.searchable_attributes.as_ref().set().map(|searchable| searchable.len()),
|
||||
|
@ -3,10 +3,9 @@ use std::str::FromStr;
|
||||
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use deserr::DeserializeFromValue;
|
||||
use index_scheduler::{IndexScheduler, Query};
|
||||
use log::debug;
|
||||
use meilisearch_types::error::ResponseError;
|
||||
use meilisearch_types::error::{ResponseError, TakeErrorMessage};
|
||||
use meilisearch_types::settings::{Settings, Unchecked};
|
||||
use meilisearch_types::star_or::StarOr;
|
||||
use meilisearch_types::tasks::{Kind, Status, Task, TaskId};
|
||||
@ -57,6 +56,14 @@ where
|
||||
{
|
||||
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;
|
||||
|
||||
@ -83,16 +90,8 @@ impl From<Task> for SummarizedTaskView {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeserializeFromValue, Deserialize, Debug, Clone, Copy)]
|
||||
#[deserr(rename_all = camelCase, deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub struct Pagination {
|
||||
#[serde(default)]
|
||||
#[deserr(default, from(&String) = FromStr::from_str -> std::num::ParseIntError)]
|
||||
pub offset: usize,
|
||||
#[serde(default = "PAGINATION_DEFAULT_LIMIT")]
|
||||
#[deserr(default = PAGINATION_DEFAULT_LIMIT(), from(&String) = FromStr::from_str -> std::num::ParseIntError)]
|
||||
pub limit: usize,
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,9 @@
|
||||
use std::fmt;
|
||||
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use deserr::{DeserializeFromValue, IntoValue, ValuePointerRef};
|
||||
use deserr::DeserializeFromValue;
|
||||
use index_scheduler::IndexScheduler;
|
||||
use meilisearch_types::error::{unwrap_any, Code, ErrorCode, ResponseError};
|
||||
use meilisearch_types::error::deserr_codes::InvalidSwapIndexes;
|
||||
use meilisearch_types::error::{DeserrError, ResponseError};
|
||||
use meilisearch_types::tasks::{IndexSwap, KindWithContent};
|
||||
use serde_json::json;
|
||||
|
||||
@ -21,14 +20,15 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
}
|
||||
|
||||
#[derive(DeserializeFromValue, Debug, Clone, PartialEq, Eq)]
|
||||
#[deserr(rename_all = camelCase, deny_unknown_fields)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
pub struct SwapIndexesPayload {
|
||||
#[deserr(error = DeserrError<InvalidSwapIndexes>)]
|
||||
indexes: Vec<String>,
|
||||
}
|
||||
|
||||
pub async fn swap_indexes(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_SWAP }>, Data<IndexScheduler>>,
|
||||
params: ValidatedJson<Vec<SwapIndexesPayload>, SwapIndexesDeserrError>,
|
||||
params: ValidatedJson<Vec<SwapIndexesPayload>, DeserrError>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
@ -62,49 +62,3 @@ pub async fn swap_indexes(
|
||||
let task: SummarizedTaskView = task.into();
|
||||
Ok(HttpResponse::Accepted().json(task))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SwapIndexesDeserrError {
|
||||
error: String,
|
||||
code: Code,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SwapIndexesDeserrError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for SwapIndexesDeserrError {}
|
||||
impl ErrorCode for SwapIndexesDeserrError {
|
||||
fn error_code(&self) -> Code {
|
||||
self.code
|
||||
}
|
||||
}
|
||||
|
||||
impl deserr::MergeWithError<SwapIndexesDeserrError> for SwapIndexesDeserrError {
|
||||
fn merge(
|
||||
_self_: Option<Self>,
|
||||
other: SwapIndexesDeserrError,
|
||||
_merge_location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
Err(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl deserr::DeserializeError for SwapIndexesDeserrError {
|
||||
fn error<V: IntoValue>(
|
||||
_self_: Option<Self>,
|
||||
error: deserr::ErrorKind<V>,
|
||||
location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
let error = unwrap_any(deserr::serde_json::JsonError::error(None, error, location)).0;
|
||||
|
||||
let code = match location.last_field() {
|
||||
Some("indexes") => Code::InvalidSwapIndexes,
|
||||
_ => Code::BadRequest,
|
||||
};
|
||||
|
||||
Err(SwapIndexesDeserrError { error, code })
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,16 +1,12 @@
|
||||
use std::cmp::min;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
||||
use std::convert::Infallible;
|
||||
use std::fmt;
|
||||
use std::num::ParseIntError;
|
||||
use std::str::{FromStr, ParseBoolError};
|
||||
use std::str::FromStr;
|
||||
use std::time::Instant;
|
||||
|
||||
use deserr::{
|
||||
DeserializeError, DeserializeFromValue, ErrorKind, IntoValue, MergeWithError, ValuePointerRef,
|
||||
};
|
||||
use deserr::DeserializeFromValue;
|
||||
use either::Either;
|
||||
use meilisearch_types::error::{unwrap_any, Code, ErrorCode};
|
||||
use meilisearch_types::error::deserr_codes::*;
|
||||
use meilisearch_types::error::DeserrError;
|
||||
use meilisearch_types::settings::DEFAULT_PAGINATION_MAX_TOTAL_HITS;
|
||||
use meilisearch_types::{milli, Document};
|
||||
use milli::tokenizer::TokenizerBuilder;
|
||||
@ -34,32 +30,41 @@ pub const DEFAULT_HIGHLIGHT_PRE_TAG: fn() -> String = || "<em>".to_string();
|
||||
pub const DEFAULT_HIGHLIGHT_POST_TAG: fn() -> String = || "</em>".to_string();
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, DeserializeFromValue)]
|
||||
#[deserr(rename_all = camelCase, deny_unknown_fields)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
pub struct SearchQuery {
|
||||
#[deserr(error = DeserrError<InvalidSearchQ>)]
|
||||
pub q: Option<String>,
|
||||
#[deserr(default = DEFAULT_SEARCH_OFFSET())]
|
||||
#[deserr(error = DeserrError<InvalidSearchOffset>, default = DEFAULT_SEARCH_OFFSET())]
|
||||
pub offset: usize,
|
||||
#[deserr(default = DEFAULT_SEARCH_LIMIT())]
|
||||
#[deserr(error = DeserrError<InvalidSearchLimit>, default = DEFAULT_SEARCH_LIMIT())]
|
||||
pub limit: usize,
|
||||
#[deserr(error = DeserrError<InvalidSearchPage>)]
|
||||
pub page: Option<usize>,
|
||||
#[deserr(error = DeserrError<InvalidSearchHitsPerPage>)]
|
||||
pub hits_per_page: Option<usize>,
|
||||
#[deserr(error = DeserrError<InvalidSearchAttributesToRetrieve>)]
|
||||
pub attributes_to_retrieve: Option<BTreeSet<String>>,
|
||||
#[deserr(error = DeserrError<InvalidSearchAttributesToCrop>)]
|
||||
pub attributes_to_crop: Option<Vec<String>>,
|
||||
#[deserr(default = DEFAULT_CROP_LENGTH())]
|
||||
#[deserr(error = DeserrError<InvalidSearchCropLength>, default = DEFAULT_CROP_LENGTH())]
|
||||
pub crop_length: usize,
|
||||
#[deserr(error = DeserrError<InvalidSearchAttributesToHighlight>)]
|
||||
pub attributes_to_highlight: Option<HashSet<String>>,
|
||||
#[deserr(default)]
|
||||
#[deserr(error = DeserrError<InvalidSearchShowMatchesPosition>, default)]
|
||||
pub show_matches_position: bool,
|
||||
#[deserr(error = DeserrError<InvalidSearchFilter>)]
|
||||
pub filter: Option<Value>,
|
||||
#[deserr(error = DeserrError<InvalidSearchSort>)]
|
||||
pub sort: Option<Vec<String>>,
|
||||
#[deserr(error = DeserrError<InvalidSearchFacets>)]
|
||||
pub facets: Option<Vec<String>>,
|
||||
#[deserr(default = DEFAULT_HIGHLIGHT_PRE_TAG())]
|
||||
#[deserr(error = DeserrError<InvalidSearchHighlightPreTag>, default = DEFAULT_HIGHLIGHT_PRE_TAG())]
|
||||
pub highlight_pre_tag: String,
|
||||
#[deserr(default = DEFAULT_HIGHLIGHT_POST_TAG())]
|
||||
#[deserr(error = DeserrError<InvalidSearchHighlightPostTag>, default = DEFAULT_HIGHLIGHT_POST_TAG())]
|
||||
pub highlight_post_tag: String,
|
||||
#[deserr(default = DEFAULT_CROP_MARKER())]
|
||||
#[deserr(error = DeserrError<InvalidSearchCropMarker>, default = DEFAULT_CROP_MARKER())]
|
||||
pub crop_marker: String,
|
||||
#[deserr(default)]
|
||||
#[deserr(error = DeserrError<InvalidSearchMatchingStrategy>, default)]
|
||||
pub matching_strategy: MatchingStrategy,
|
||||
}
|
||||
|
||||
@ -94,96 +99,6 @@ impl From<MatchingStrategy> for TermsMatchingStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SearchDeserError {
|
||||
error: String,
|
||||
code: Code,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SearchDeserError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.error)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for SearchDeserError {}
|
||||
impl ErrorCode for SearchDeserError {
|
||||
fn error_code(&self) -> Code {
|
||||
self.code
|
||||
}
|
||||
}
|
||||
|
||||
impl MergeWithError<SearchDeserError> for SearchDeserError {
|
||||
fn merge(
|
||||
_self_: Option<Self>,
|
||||
other: SearchDeserError,
|
||||
_merge_location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
Err(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl DeserializeError for SearchDeserError {
|
||||
fn error<V: IntoValue>(
|
||||
_self_: Option<Self>,
|
||||
error: ErrorKind<V>,
|
||||
location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
let error = unwrap_any(deserr::serde_json::JsonError::error(None, error, location)).0;
|
||||
|
||||
let code = match location.last_field() {
|
||||
Some("q") => Code::InvalidSearchQ,
|
||||
Some("offset") => Code::InvalidSearchOffset,
|
||||
Some("limit") => Code::InvalidSearchLimit,
|
||||
Some("page") => Code::InvalidSearchPage,
|
||||
Some("hitsPerPage") => Code::InvalidSearchHitsPerPage,
|
||||
Some("attributesToRetrieve") => Code::InvalidSearchAttributesToRetrieve,
|
||||
Some("attributesToCrop") => Code::InvalidSearchAttributesToCrop,
|
||||
Some("cropLength") => Code::InvalidSearchCropLength,
|
||||
Some("attributesToHighlight") => Code::InvalidSearchAttributesToHighlight,
|
||||
Some("showMatchesPosition") => Code::InvalidSearchShowMatchesPosition,
|
||||
Some("filter") => Code::InvalidSearchFilter,
|
||||
Some("sort") => Code::InvalidSearchSort,
|
||||
Some("facets") => Code::InvalidSearchFacets,
|
||||
Some("highlightPreTag") => Code::InvalidSearchHighlightPreTag,
|
||||
Some("highlightPostTag") => Code::InvalidSearchHighlightPostTag,
|
||||
Some("cropMarker") => Code::InvalidSearchCropMarker,
|
||||
Some("matchingStrategy") => Code::InvalidSearchMatchingStrategy,
|
||||
_ => Code::BadRequest,
|
||||
};
|
||||
|
||||
Err(SearchDeserError { error, code })
|
||||
}
|
||||
}
|
||||
|
||||
impl MergeWithError<ParseBoolError> for SearchDeserError {
|
||||
fn merge(
|
||||
_self_: Option<Self>,
|
||||
other: ParseBoolError,
|
||||
merge_location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
SearchDeserError::error::<Infallible>(
|
||||
None,
|
||||
ErrorKind::Unexpected { msg: other.to_string() },
|
||||
merge_location,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl MergeWithError<ParseIntError> for SearchDeserError {
|
||||
fn merge(
|
||||
_self_: Option<Self>,
|
||||
other: ParseIntError,
|
||||
merge_location: ValuePointerRef,
|
||||
) -> Result<Self, Self> {
|
||||
SearchDeserError::error::<Infallible>(
|
||||
None,
|
||||
ErrorKind::Unexpected { msg: other.to_string() },
|
||||
merge_location,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
|
||||
pub struct SearchHit {
|
||||
#[serde(flatten)]
|
||||
@ -695,7 +610,7 @@ fn parse_filter(facets: &Value) -> Result<Option<Filter>, MeilisearchHttpError>
|
||||
Ok(condition)
|
||||
}
|
||||
Value::Array(arr) => parse_filter_array(arr),
|
||||
v => Err(MeilisearchHttpError::InvalidExpression(&["Array"], v.clone())),
|
||||
v => Err(MeilisearchHttpError::InvalidExpression(&["String", "Array"], v.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -197,6 +197,76 @@ impl Index<'_> {
|
||||
self.service.patch_encoded(url, settings, self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn update_settings_displayed_attributes(
|
||||
&self,
|
||||
settings: Value,
|
||||
) -> (Value, StatusCode) {
|
||||
let url =
|
||||
format!("/indexes/{}/settings/displayed-attributes", urlencode(self.uid.as_ref()));
|
||||
self.service.put_encoded(url, settings, self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn update_settings_searchable_attributes(
|
||||
&self,
|
||||
settings: Value,
|
||||
) -> (Value, StatusCode) {
|
||||
let url =
|
||||
format!("/indexes/{}/settings/searchable-attributes", urlencode(self.uid.as_ref()));
|
||||
self.service.put_encoded(url, settings, self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn update_settings_filterable_attributes(
|
||||
&self,
|
||||
settings: Value,
|
||||
) -> (Value, StatusCode) {
|
||||
let url =
|
||||
format!("/indexes/{}/settings/filterable-attributes", urlencode(self.uid.as_ref()));
|
||||
self.service.put_encoded(url, settings, self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn update_settings_sortable_attributes(
|
||||
&self,
|
||||
settings: Value,
|
||||
) -> (Value, StatusCode) {
|
||||
let url = format!("/indexes/{}/settings/sortable-attributes", urlencode(self.uid.as_ref()));
|
||||
self.service.put_encoded(url, settings, self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn update_settings_ranking_rules(&self, settings: Value) -> (Value, StatusCode) {
|
||||
let url = format!("/indexes/{}/settings/ranking-rules", urlencode(self.uid.as_ref()));
|
||||
self.service.put_encoded(url, settings, self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn update_settings_stop_words(&self, settings: Value) -> (Value, StatusCode) {
|
||||
let url = format!("/indexes/{}/settings/stop-words", urlencode(self.uid.as_ref()));
|
||||
self.service.put_encoded(url, settings, self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn update_settings_synonyms(&self, settings: Value) -> (Value, StatusCode) {
|
||||
let url = format!("/indexes/{}/settings/synonyms", urlencode(self.uid.as_ref()));
|
||||
self.service.put_encoded(url, settings, self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn update_settings_distinct_attribute(&self, settings: Value) -> (Value, StatusCode) {
|
||||
let url = format!("/indexes/{}/settings/distinct-attribute", urlencode(self.uid.as_ref()));
|
||||
self.service.put_encoded(url, settings, self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn update_settings_typo_tolerance(&self, settings: Value) -> (Value, StatusCode) {
|
||||
let url = format!("/indexes/{}/settings/typo-tolerance", urlencode(self.uid.as_ref()));
|
||||
self.service.patch_encoded(url, settings, self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn update_settings_faceting(&self, settings: Value) -> (Value, StatusCode) {
|
||||
let url = format!("/indexes/{}/settings/faceting", urlencode(self.uid.as_ref()));
|
||||
self.service.patch_encoded(url, settings, self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn update_settings_pagination(&self, settings: Value) -> (Value, StatusCode) {
|
||||
let url = format!("/indexes/{}/settings/pagination", urlencode(self.uid.as_ref()));
|
||||
self.service.patch_encoded(url, settings, self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn delete_settings(&self) -> (Value, StatusCode) {
|
||||
let url = format!("/indexes/{}/settings", urlencode(self.uid.as_ref()));
|
||||
self.service.delete(url).await
|
||||
|
@ -926,7 +926,7 @@ async fn error_primary_key_inference() {
|
||||
"indexedDocuments": 1
|
||||
},
|
||||
"error": {
|
||||
"message": "The primary key inference process failed because the engine did not find any field ending with `id` in its name. Please specify the primary key manually using the `primaryKey` query parameter.",
|
||||
"message": "The primary key inference failed as the engine did not find any field ending with `id` in its name. Please specify the primary key manually using the `primaryKey` query parameter.",
|
||||
"code": "index_primary_key_no_candidate_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-primary-key-no-candidate-found"
|
||||
@ -966,7 +966,7 @@ async fn error_primary_key_inference() {
|
||||
"indexedDocuments": 1
|
||||
},
|
||||
"error": {
|
||||
"message": "The primary key inference process failed because the engine found 3 fields ending with `id` in their name, such as 'id' and 'object_id'. Please specify the primary key manually using the `primaryKey` query parameter.",
|
||||
"message": "The primary key inference failed as the engine found 3 fields ending with `id` in their names: 'id' and 'object_id'. Please specify the primary key manually using the `primaryKey` query parameter.",
|
||||
"code": "index_primary_key_multiple_candidates_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-primary-key-multiple-candidates-found"
|
||||
|
@ -1,3 +1,4 @@
|
||||
use meili_snap::*;
|
||||
use serde_json::json;
|
||||
|
||||
use super::DOCUMENTS;
|
||||
@ -37,104 +38,368 @@ async fn search_unexisting_parameter() {
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_invalid_crop_marker() {
|
||||
async fn search_bad_q() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
// object
|
||||
let response = index.search_post(json!({"cropMarker": { "marker": "<crop>" }})).await;
|
||||
meili_snap::snapshot!(format!("{:#?}", response), @r###"
|
||||
(
|
||||
Object {
|
||||
"message": String("invalid type: Map `{\"marker\":\"<crop>\"}`, expected a String at `.cropMarker`."),
|
||||
"code": String("invalid_search_crop_marker"),
|
||||
"type": String("invalid_request"),
|
||||
"link": String("https://docs.meilisearch.com/errors#invalid-search-crop-marker"),
|
||||
},
|
||||
400,
|
||||
)
|
||||
let (response, code) = index.search_post(json!({"q": ["doggo"]})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at `.q`.",
|
||||
"code": "invalid_search_q",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-q"
|
||||
}
|
||||
"###);
|
||||
// Can't make the `q` fail with a get search since it'll accept anything as a string.
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_bad_offset() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.search_post(json!({"offset": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Integer at `.offset`.",
|
||||
"code": "invalid_search_offset",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-offset"
|
||||
}
|
||||
"###);
|
||||
|
||||
// array
|
||||
let response = index.search_post(json!({"cropMarker": ["marker", "<crop>"]})).await;
|
||||
meili_snap::snapshot!(format!("{:#?}", response), @r###"
|
||||
(
|
||||
Object {
|
||||
"message": String("invalid type: Sequence `[\"marker\",\"<crop>\"]`, expected a String at `.cropMarker`."),
|
||||
"code": String("invalid_search_crop_marker"),
|
||||
"type": String("invalid_request"),
|
||||
"link": String("https://docs.meilisearch.com/errors#invalid-search-crop-marker"),
|
||||
},
|
||||
400,
|
||||
)
|
||||
let (response, code) = index.search_get(json!({"offset": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.offset`.",
|
||||
"code": "invalid_search_offset",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-offset"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_invalid_highlight_pre_tag() {
|
||||
async fn search_bad_limit() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
// object
|
||||
let response = index.search_post(json!({"highlightPreTag": { "marker": "<em>" }})).await;
|
||||
meili_snap::snapshot!(format!("{:#?}", response), @r###"
|
||||
(
|
||||
Object {
|
||||
"message": String("invalid type: Map `{\"marker\":\"<em>\"}`, expected a String at `.highlightPreTag`."),
|
||||
"code": String("invalid_search_highlight_pre_tag"),
|
||||
"type": String("invalid_request"),
|
||||
"link": String("https://docs.meilisearch.com/errors#invalid-search-highlight-pre-tag"),
|
||||
},
|
||||
400,
|
||||
)
|
||||
let (response, code) = index.search_post(json!({"limit": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Integer at `.limit`.",
|
||||
"code": "invalid_search_limit",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-limit"
|
||||
}
|
||||
"###);
|
||||
|
||||
// array
|
||||
let response = index.search_post(json!({"highlightPreTag": ["marker", "<em>"]})).await;
|
||||
meili_snap::snapshot!(format!("{:#?}", response), @r###"
|
||||
(
|
||||
Object {
|
||||
"message": String("invalid type: Sequence `[\"marker\",\"<em>\"]`, expected a String at `.highlightPreTag`."),
|
||||
"code": String("invalid_search_highlight_pre_tag"),
|
||||
"type": String("invalid_request"),
|
||||
"link": String("https://docs.meilisearch.com/errors#invalid-search-highlight-pre-tag"),
|
||||
},
|
||||
400,
|
||||
)
|
||||
let (response, code) = index.search_get(json!({"limit": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.limit`.",
|
||||
"code": "invalid_search_limit",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-limit"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_invalid_highlight_post_tag() {
|
||||
async fn search_bad_page() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
// object
|
||||
let response = index.search_post(json!({"highlightPostTag": { "marker": "</em>" }})).await;
|
||||
meili_snap::snapshot!(format!("{:#?}", response), @r###"
|
||||
(
|
||||
Object {
|
||||
"message": String("invalid type: Map `{\"marker\":\"</em>\"}`, expected a String at `.highlightPostTag`."),
|
||||
"code": String("invalid_search_highlight_post_tag"),
|
||||
"type": String("invalid_request"),
|
||||
"link": String("https://docs.meilisearch.com/errors#invalid-search-highlight-post-tag"),
|
||||
},
|
||||
400,
|
||||
)
|
||||
let (response, code) = index.search_post(json!({"page": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Integer at `.page`.",
|
||||
"code": "invalid_search_page",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-page"
|
||||
}
|
||||
"###);
|
||||
|
||||
// array
|
||||
let response = index.search_post(json!({"highlightPostTag": ["marker", "</em>"]})).await;
|
||||
meili_snap::snapshot!(format!("{:#?}", response), @r###"
|
||||
(
|
||||
Object {
|
||||
"message": String("invalid type: Sequence `[\"marker\",\"</em>\"]`, expected a String at `.highlightPostTag`."),
|
||||
"code": String("invalid_search_highlight_post_tag"),
|
||||
"type": String("invalid_request"),
|
||||
"link": String("https://docs.meilisearch.com/errors#invalid-search-highlight-post-tag"),
|
||||
},
|
||||
400,
|
||||
)
|
||||
let (response, code) = index.search_get(json!({"page": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.page`.",
|
||||
"code": "invalid_search_page",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-page"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_bad_hits_per_page() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.search_post(json!({"hitsPerPage": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Integer at `.hitsPerPage`.",
|
||||
"code": "invalid_search_hits_per_page",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-hits-per-page"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.search_get(json!({"hitsPerPage": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.hitsPerPage`.",
|
||||
"code": "invalid_search_hits_per_page",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-hits-per-page"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_bad_attributes_to_crop() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.search_post(json!({"attributesToCrop": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.attributesToCrop`.",
|
||||
"code": "invalid_search_attributes_to_crop",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-attributes-to-crop"
|
||||
}
|
||||
"###);
|
||||
// Can't make the `attributes_to_crop` fail with a get search since it'll accept anything as an array of strings.
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_bad_crop_length() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.search_post(json!({"cropLength": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Integer at `.cropLength`.",
|
||||
"code": "invalid_search_crop_length",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-crop-length"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.search_get(json!({"cropLength": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.cropLength`.",
|
||||
"code": "invalid_search_crop_length",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-crop-length"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_bad_attributes_to_highlight() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.search_post(json!({"attributesToHighlight": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.attributesToHighlight`.",
|
||||
"code": "invalid_search_attributes_to_highlight",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-attributes-to-highlight"
|
||||
}
|
||||
"###);
|
||||
// Can't make the `attributes_to_highlight` fail with a get search since it'll accept anything as an array of strings.
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_bad_filter() {
|
||||
// Since a filter is deserialized as a json Value it will never fail to deserialize.
|
||||
// Thus the error message is not generated by deserr but written by us.
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
// Also, to trigger the error message we need to effectively create the index or else it'll throw an
|
||||
// index does not exists error.
|
||||
let (_, code) = index.create(None).await;
|
||||
server.wait_task(0).await;
|
||||
|
||||
snapshot!(code, @"202 Accepted");
|
||||
|
||||
let (response, code) = index.search_post(json!({ "filter": true })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid syntax for the filter parameter: `expected String, Array, found: true`.",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
}
|
||||
"###);
|
||||
// Can't make the `filter` fail with a get search since it'll accept anything as a strings.
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_bad_sort() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.search_post(json!({"sort": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.sort`.",
|
||||
"code": "invalid_search_sort",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-sort"
|
||||
}
|
||||
"###);
|
||||
// Can't make the `sort` fail with a get search since it'll accept anything as a strings.
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_bad_show_matches_position() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.search_post(json!({"showMatchesPosition": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Boolean at `.showMatchesPosition`.",
|
||||
"code": "invalid_search_show_matches_position",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-show-matches-position"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.search_get(json!({"showMatchesPosition": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "provided string was not `true` or `false` at `.showMatchesPosition`.",
|
||||
"code": "invalid_search_show_matches_position",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-show-matches-position"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_bad_facets() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.search_post(json!({"facets": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.facets`.",
|
||||
"code": "invalid_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-facets"
|
||||
}
|
||||
"###);
|
||||
// Can't make the `attributes_to_highlight` fail with a get search since it'll accept anything as an array of strings.
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_bad_highlight_pre_tag() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.search_post(json!({"highlightPreTag": ["doggo"]})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at `.highlightPreTag`.",
|
||||
"code": "invalid_search_highlight_pre_tag",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-highlight-pre-tag"
|
||||
}
|
||||
"###);
|
||||
// Can't make the `highlight_pre_tag` fail with a get search since it'll accept anything as a strings.
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_bad_highlight_post_tag() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.search_post(json!({"highlightPostTag": ["doggo"]})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at `.highlightPostTag`.",
|
||||
"code": "invalid_search_highlight_post_tag",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-highlight-post-tag"
|
||||
}
|
||||
"###);
|
||||
// Can't make the `highlight_post_tag` fail with a get search since it'll accept anything as a strings.
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_bad_crop_marker() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.search_post(json!({"cropMarker": ["doggo"]})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at `.cropMarker`.",
|
||||
"code": "invalid_search_crop_marker",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-crop-marker"
|
||||
}
|
||||
"###);
|
||||
// Can't make the `crop_marker` fail with a get search since it'll accept anything as a strings.
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_bad_matching_strategy() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.search_post(json!({"matchingStrategy": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: unknown value `doggo`, expected one of `last`, `all` at `.matchingStrategy`.",
|
||||
"code": "invalid_search_matching_strategy",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-matching-strategy"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.search_get(json!({"matchingStrategy": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: unknown value `doggo`, expected one of `last`, `all` at `.matchingStrategy`.",
|
||||
"code": "invalid_search_matching_strategy",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-matching-strategy"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
@ -151,9 +416,9 @@ async fn filter_invalid_syntax_object() {
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "Was expecting an operation `=`, `!=`, `>=`, `>`, `<=`, `<`, `IN`, `NOT IN`, `TO`, `EXISTS`, `NOT EXISTS`, or `_geoRadius` at `title & Glass`.\n1:14 title & Glass",
|
||||
"code": "invalid_filter",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": "title & Glass"}), |response, code| {
|
||||
@ -176,9 +441,9 @@ async fn filter_invalid_syntax_array() {
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "Was expecting an operation `=`, `!=`, `>=`, `>`, `<=`, `<`, `IN`, `NOT IN`, `TO`, `EXISTS`, `NOT EXISTS`, or `_geoRadius` at `title & Glass`.\n1:14 title & Glass",
|
||||
"code": "invalid_filter",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": ["title & Glass"]}), |response, code| {
|
||||
@ -201,9 +466,9 @@ async fn filter_invalid_syntax_string() {
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "Found unexpected characters at the end of the filter: `XOR title = Glass`. You probably forgot an `OR` or an `AND` rule.\n15:32 title = Glass XOR title = Glass",
|
||||
"code": "invalid_filter",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": "title = Glass XOR title = Glass"}), |response, code| {
|
||||
@ -226,9 +491,9 @@ async fn filter_invalid_attribute_array() {
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "Attribute `many` is not filterable. Available filterable attributes are: `title`.\n1:5 many = Glass",
|
||||
"code": "invalid_filter",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": ["many = Glass"]}), |response, code| {
|
||||
@ -251,9 +516,9 @@ async fn filter_invalid_attribute_string() {
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "Attribute `many` is not filterable. Available filterable attributes are: `title`.\n1:5 many = Glass",
|
||||
"code": "invalid_filter",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": "many = Glass"}), |response, code| {
|
||||
@ -276,9 +541,9 @@ async fn filter_reserved_geo_attribute_array() {
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "`_geo` is a reserved keyword and thus can't be used as a filter expression. Use the _geoRadius(latitude, longitude, distance) built-in rule to filter on _geo field coordinates.\n1:5 _geo = Glass",
|
||||
"code": "invalid_filter",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": ["_geo = Glass"]}), |response, code| {
|
||||
@ -301,9 +566,9 @@ async fn filter_reserved_geo_attribute_string() {
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "`_geo` is a reserved keyword and thus can't be used as a filter expression. Use the _geoRadius(latitude, longitude, distance) built-in rule to filter on _geo field coordinates.\n1:5 _geo = Glass",
|
||||
"code": "invalid_filter",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": "_geo = Glass"}), |response, code| {
|
||||
@ -326,9 +591,9 @@ async fn filter_reserved_attribute_array() {
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "`_geoDistance` is a reserved keyword and thus can't be used as a filter expression.\n1:13 _geoDistance = Glass",
|
||||
"code": "invalid_filter",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": ["_geoDistance = Glass"]}), |response, code| {
|
||||
@ -351,9 +616,9 @@ async fn filter_reserved_attribute_string() {
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "`_geoDistance` is a reserved keyword and thus can't be used as a filter expression.\n1:13 _geoDistance = Glass",
|
||||
"code": "invalid_filter",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": "_geoDistance = Glass"}), |response, code| {
|
||||
@ -376,9 +641,9 @@ async fn sort_geo_reserved_attribute() {
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "`_geo` is a reserved keyword and thus can't be used as a sort expression. Use the _geoPoint(latitude, longitude) built-in rule to sort on _geo field coordinates.",
|
||||
"code": "invalid_sort",
|
||||
"code": "invalid_search_sort",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-sort"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-sort"
|
||||
});
|
||||
index
|
||||
.search(
|
||||
@ -406,9 +671,9 @@ async fn sort_reserved_attribute() {
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "`_geoDistance` is a reserved keyword and thus can't be used as a sort expression.",
|
||||
"code": "invalid_sort",
|
||||
"code": "invalid_search_sort",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-sort"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-sort"
|
||||
});
|
||||
index
|
||||
.search(
|
||||
@ -436,9 +701,9 @@ async fn sort_unsortable_attribute() {
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "Attribute `title` is not sortable. Available sortable attributes are: `id`.",
|
||||
"code": "invalid_sort",
|
||||
"code": "invalid_search_sort",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-sort"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-sort"
|
||||
});
|
||||
index
|
||||
.search(
|
||||
@ -466,9 +731,9 @@ async fn sort_invalid_syntax() {
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "Invalid syntax for the sort parameter: expected expression ending by `:asc` or `:desc`, found `title`.",
|
||||
"code": "invalid_sort",
|
||||
"code": "invalid_search_sort",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-sort"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-sort"
|
||||
});
|
||||
index
|
||||
.search(
|
||||
@ -500,9 +765,9 @@ async fn sort_unset_ranking_rule() {
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "The sort ranking rule must be specified in the ranking rules settings to use the sort parameter at search time.",
|
||||
"code": "invalid_sort",
|
||||
"code": "invalid_search_sort",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-sort"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-sort"
|
||||
});
|
||||
index
|
||||
.search(
|
||||
|
@ -200,11 +200,14 @@ async fn search_with_filter_string_notation() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
index.update_settings(json!({"filterableAttributes": ["title"]})).await;
|
||||
let (_, code) = index.update_settings(json!({"filterableAttributes": ["title"]})).await;
|
||||
meili_snap::snapshot!(code, @"202 Accepted");
|
||||
|
||||
let documents = DOCUMENTS.clone();
|
||||
index.add_documents(documents, None).await;
|
||||
index.wait_task(1).await;
|
||||
let (_, code) = index.add_documents(documents, None).await;
|
||||
meili_snap::snapshot!(code, @"202 Accepted");
|
||||
let res = index.wait_task(1).await;
|
||||
meili_snap::snapshot!(res["status"], @r###""succeeded""###);
|
||||
|
||||
index
|
||||
.search(
|
||||
@ -220,11 +223,15 @@ async fn search_with_filter_string_notation() {
|
||||
|
||||
let index = server.index("nested");
|
||||
|
||||
index.update_settings(json!({"filterableAttributes": ["cattos", "doggos.age"]})).await;
|
||||
let (_, code) =
|
||||
index.update_settings(json!({"filterableAttributes": ["cattos", "doggos.age"]})).await;
|
||||
meili_snap::snapshot!(code, @"202 Accepted");
|
||||
|
||||
let documents = NESTED_DOCUMENTS.clone();
|
||||
index.add_documents(documents, None).await;
|
||||
index.wait_task(3).await;
|
||||
let (_, code) = index.add_documents(documents, None).await;
|
||||
meili_snap::snapshot!(code, @"202 Accepted");
|
||||
let res = index.wait_task(3).await;
|
||||
meili_snap::snapshot!(res["status"], @r###""succeeded""###);
|
||||
|
||||
index
|
||||
.search(
|
||||
|
312
meilisearch/tests/settings/errors.rs
Normal file
312
meilisearch/tests/settings/errors.rs
Normal file
@ -0,0 +1,312 @@
|
||||
use meili_snap::*;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::Server;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn settings_bad_displayed_attributes() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.update_settings(json!({ "displayedAttributes": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.displayedAttributes`.",
|
||||
"code": "invalid_settings_displayed_attributes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-displayed-attributes"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.update_settings_displayed_attributes(json!("doggo")).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.",
|
||||
"code": "invalid_settings_displayed_attributes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-displayed-attributes"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn settings_bad_searchable_attributes() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.update_settings(json!({ "searchableAttributes": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.searchableAttributes`.",
|
||||
"code": "invalid_settings_searchable_attributes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-searchable-attributes"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.update_settings_searchable_attributes(json!("doggo")).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.",
|
||||
"code": "invalid_settings_searchable_attributes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-searchable-attributes"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn settings_bad_filterable_attributes() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.update_settings(json!({ "filterableAttributes": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.filterableAttributes`.",
|
||||
"code": "invalid_settings_filterable_attributes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-filterable-attributes"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.update_settings_filterable_attributes(json!("doggo")).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.",
|
||||
"code": "invalid_settings_filterable_attributes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-filterable-attributes"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn settings_bad_sortable_attributes() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.update_settings(json!({ "sortableAttributes": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.sortableAttributes`.",
|
||||
"code": "invalid_settings_sortable_attributes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-sortable-attributes"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.update_settings_sortable_attributes(json!("doggo")).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.",
|
||||
"code": "invalid_settings_sortable_attributes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-sortable-attributes"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn settings_bad_ranking_rules() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.update_settings(json!({ "rankingRules": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.rankingRules`.",
|
||||
"code": "invalid_settings_ranking_rules",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-ranking-rules"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.update_settings_ranking_rules(json!("doggo")).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.",
|
||||
"code": "invalid_settings_ranking_rules",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-ranking-rules"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn settings_bad_stop_words() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.update_settings(json!({ "stopWords": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.stopWords`.",
|
||||
"code": "invalid_settings_stop_words",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-stop-words"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.update_settings_stop_words(json!("doggo")).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.",
|
||||
"code": "invalid_settings_stop_words",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-stop-words"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn settings_bad_synonyms() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.update_settings(json!({ "synonyms": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Map at `.synonyms`.",
|
||||
"code": "invalid_settings_synonyms",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-synonyms"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.update_settings_synonyms(json!("doggo")).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Map at ``.",
|
||||
"code": "invalid_settings_synonyms",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-synonyms"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn settings_bad_distinct_attribute() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.update_settings(json!({ "distinctAttribute": ["doggo"] })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at `.distinctAttribute`.",
|
||||
"code": "invalid_settings_distinct_attribute",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-distinct-attribute"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.update_settings_distinct_attribute(json!(["doggo"])).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at ``.",
|
||||
"code": "invalid_settings_distinct_attribute",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-distinct-attribute"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn settings_bad_typo_tolerance() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.update_settings(json!({ "typoTolerance": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Map at `.typoTolerance`.",
|
||||
"code": "invalid_settings_typo_tolerance",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-typo-tolerance"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.update_settings_typo_tolerance(json!("doggo")).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Map at ``.",
|
||||
"code": "invalid_settings_typo_tolerance",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-typo-tolerance"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn settings_bad_faceting() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.update_settings(json!({ "faceting": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Map at `.faceting`.",
|
||||
"code": "invalid_settings_faceting",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-faceting"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.update_settings_faceting(json!("doggo")).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Map at ``.",
|
||||
"code": "invalid_settings_faceting",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-faceting"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn settings_bad_pagination() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.update_settings(json!({ "pagination": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Map at `.pagination`.",
|
||||
"code": "invalid_settings_pagination",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-pagination"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.update_settings_pagination(json!("doggo")).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Map at ``.",
|
||||
"code": "invalid_settings_pagination",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-pagination"
|
||||
}
|
||||
"###);
|
||||
}
|
@ -179,15 +179,15 @@ async fn error_update_setting_unexisting_index_invalid_uid() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test##! ");
|
||||
let (response, code) = index.update_settings(json!({})).await;
|
||||
assert_eq!(code, 400);
|
||||
|
||||
let expected = json!({
|
||||
"message": "`test##! ` 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",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-index-uid"});
|
||||
|
||||
assert_eq!(response, expected);
|
||||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "`test##! ` 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",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-index-uid"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
macro_rules! test_setting_routes {
|
||||
@ -278,22 +278,16 @@ async fn error_set_invalid_ranking_rules() {
|
||||
let index = server.index("test");
|
||||
index.create(None).await;
|
||||
|
||||
let (_response, _code) =
|
||||
index.update_settings(json!({ "rankingRules": [ "manyTheFish"]})).await;
|
||||
index.wait_task(1).await;
|
||||
let (response, code) = index.get_task(1).await;
|
||||
|
||||
assert_eq!(code, 200);
|
||||
assert_eq!(response["status"], "failed");
|
||||
|
||||
let expected_error = json!({
|
||||
"message": r#"`manyTheFish` ranking rule is invalid. Valid ranking rules are words, typo, sort, proximity, attribute, exactness and custom ranking rules."#,
|
||||
"code": "invalid_ranking_rule",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-ranking-rule"
|
||||
});
|
||||
|
||||
assert_eq!(response["error"], expected_error);
|
||||
let (response, code) = index.update_settings(json!({ "rankingRules": [ "manyTheFish"]})).await;
|
||||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
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]`.",
|
||||
"code": "invalid_settings_ranking_rules",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-ranking-rules"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
|
@ -1,2 +1,3 @@
|
||||
mod distinct;
|
||||
mod errors;
|
||||
mod get_settings;
|
||||
|
498
meilisearch/tests/tasks/errors.rs
Normal file
498
meilisearch/tests/tasks/errors.rs
Normal file
@ -0,0 +1,498 @@
|
||||
use meili_snap::*;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::Server;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn task_bad_uids() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"uids": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.uids`.",
|
||||
"code": "invalid_task_uids",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-uids"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"uids": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.uids`.",
|
||||
"code": "invalid_task_uids",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-uids"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"uids": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.uids`.",
|
||||
"code": "invalid_task_uids",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-uids"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn task_bad_canceled_by() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"canceledBy": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.canceledBy`.",
|
||||
"code": "invalid_task_canceled_by",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-canceled-by"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"canceledBy": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.canceledBy`.",
|
||||
"code": "invalid_task_canceled_by",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-canceled-by"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"canceledBy": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.canceledBy`.",
|
||||
"code": "invalid_task_canceled_by",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-canceled-by"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn task_bad_types() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"types": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_types",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-types"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"types": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_types",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-types"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"types": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_types",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-types"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn task_bad_statuses() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"statuses": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is not a status. Available status are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. at `.statuses`.",
|
||||
"code": "invalid_task_statuses",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-statuses"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"statuses": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is not a status. Available status are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. at `.statuses`.",
|
||||
"code": "invalid_task_statuses",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-statuses"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"statuses": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is not a status. Available status are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. at `.statuses`.",
|
||||
"code": "invalid_task_statuses",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-statuses"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn task_bad_index_uids() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"indexUids": "the good doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_index_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-index-uid"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"indexUids": "the good doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_index_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-index-uid"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"indexUids": "the good doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_index_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-index-uid"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn task_bad_limit() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"limit": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.limit`.",
|
||||
"code": "invalid_task_limit",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-limit"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"limit": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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 ``.",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"limit": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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 ``.",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn task_bad_from() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"from": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.from`.",
|
||||
"code": "invalid_task_from",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-from"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"from": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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 ``.",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"from": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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 ``.",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn task_bad_after_enqueued_at() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"afterEnqueuedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_after_enqueued_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-enqueued-at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"afterEnqueuedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_after_enqueued_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-enqueued-at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"afterEnqueuedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_after_enqueued_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-enqueued-at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn task_bad_before_enqueued_at() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"beforeEnqueuedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_before_enqueued_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-enqueued-at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"beforeEnqueuedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_before_enqueued_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-enqueued-at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"beforeEnqueuedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_before_enqueued_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-enqueued-at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn task_bad_after_started_at() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"afterStartedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_after_started_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-started-at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"afterStartedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_after_started_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-started-at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"afterStartedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_after_started_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-started-at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn task_bad_before_started_at() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"beforeStartedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_before_started_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-started-at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"beforeStartedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_before_started_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-started-at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"beforeStartedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_before_started_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-started-at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn task_bad_after_finished_at() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"afterFinishedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_after_finished_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-finished-at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"afterFinishedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_after_finished_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-finished-at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"afterFinishedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_after_finished_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-finished-at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn task_bad_before_finished_at() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"beforeFinishedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_before_finished_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-finished-at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"beforeFinishedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_before_finished_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-finished-at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"beforeFinishedAt": "doggo"})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
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`.",
|
||||
"code": "invalid_task_before_finished_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-finished-at"
|
||||
}
|
||||
"###);
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
use meili_snap::insta::{self, assert_json_snapshot};
|
||||
mod errors;
|
||||
|
||||
use meili_snap::insta::assert_json_snapshot;
|
||||
use serde_json::json;
|
||||
use time::format_description::well_known::Rfc3339;
|
||||
use time::OffsetDateTime;
|
||||
@ -179,9 +181,9 @@ async fn get_task_filter_error() {
|
||||
|
||||
let (response, code) = server.tasks_filter(json!( { "lol": "pied" })).await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
insta::assert_json_snapshot!(response, @r###"
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "Query deserialize error: unknown field `lol`",
|
||||
"message": "Json deserialize error: unknown field `lol`, expected one of `limit`, `from`, `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
@ -190,9 +192,9 @@ async fn get_task_filter_error() {
|
||||
|
||||
let (response, code) = server.tasks_filter(json!( { "uids": "pied" })).await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
insta::assert_json_snapshot!(response, @r###"
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "Task uid `pied` is invalid. It should only contain numeric characters.",
|
||||
"message": "invalid digit found in string at `.uids`.",
|
||||
"code": "invalid_task_uids",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-uids"
|
||||
@ -201,20 +203,20 @@ async fn get_task_filter_error() {
|
||||
|
||||
let (response, code) = server.tasks_filter(json!( { "from": "pied" })).await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
insta::assert_json_snapshot!(response, @r###"
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "Query deserialize error: invalid digit found in string",
|
||||
"code": "bad_request",
|
||||
"message": "invalid digit found in string at `.from`.",
|
||||
"code": "invalid_task_from",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-from"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.tasks_filter(json!( { "beforeStartedAt": "pied" })).await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
insta::assert_json_snapshot!(response, @r###"
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "Task `beforeStartedAt` `pied` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"message": "`pied` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeStartedAt`.",
|
||||
"code": "invalid_task_before_started_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-started-at"
|
||||
@ -228,7 +230,7 @@ async fn delete_task_filter_error() {
|
||||
|
||||
let (response, code) = server.delete_tasks(json!(null)).await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
insta::assert_json_snapshot!(response, @r###"
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "Query parameters to filter the tasks to delete are missing. Available query parameters are: `uids`, `indexUids`, `statuses`, `types`, `beforeEnqueuedAt`, `afterEnqueuedAt`, `beforeStartedAt`, `afterStartedAt`, `beforeFinishedAt`, `afterFinishedAt`.",
|
||||
"code": "missing_task_filters",
|
||||
@ -239,9 +241,9 @@ async fn delete_task_filter_error() {
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({ "lol": "pied" })).await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
insta::assert_json_snapshot!(response, @r###"
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "Query deserialize error: unknown field `lol`",
|
||||
"message": "Json deserialize error: unknown field `lol`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
@ -250,9 +252,9 @@ async fn delete_task_filter_error() {
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({ "uids": "pied" })).await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
insta::assert_json_snapshot!(response, @r###"
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "Task uid `pied` is invalid. It should only contain numeric characters.",
|
||||
"message": "invalid digit found in string at `.uids`.",
|
||||
"code": "invalid_task_uids",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-uids"
|
||||
@ -266,7 +268,7 @@ async fn cancel_task_filter_error() {
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!(null)).await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
insta::assert_json_snapshot!(response, @r###"
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "Query parameters to filter the tasks to cancel are missing. Available query parameters are: `uids`, `indexUids`, `statuses`, `types`, `beforeEnqueuedAt`, `afterEnqueuedAt`, `beforeStartedAt`, `afterStartedAt`, `beforeFinishedAt`, `afterFinishedAt`.",
|
||||
"code": "missing_task_filters",
|
||||
@ -277,9 +279,9 @@ async fn cancel_task_filter_error() {
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({ "lol": "pied" })).await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
insta::assert_json_snapshot!(response, @r###"
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "Query deserialize error: unknown field `lol`",
|
||||
"message": "Json deserialize error: unknown field `lol`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
@ -288,9 +290,9 @@ async fn cancel_task_filter_error() {
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({ "uids": "pied" })).await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
insta::assert_json_snapshot!(response, @r###"
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "Task uid `pied` is invalid. It should only contain numeric characters.",
|
||||
"message": "invalid digit found in string at `.uids`.",
|
||||
"code": "invalid_task_uids",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-uids"
|
||||
@ -517,46 +519,26 @@ async fn test_summarized_settings_update() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
// here we should find my payload even in the failed task.
|
||||
index.update_settings(json!({ "rankingRules": ["custom"] })).await;
|
||||
let (response, code) = index.update_settings(json!({ "rankingRules": ["custom"] })).await;
|
||||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
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]`.",
|
||||
"code": "invalid_settings_ranking_rules",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-ranking-rules"
|
||||
}
|
||||
"###);
|
||||
|
||||
index.update_settings(json!({ "displayedAttributes": ["doggos", "name"], "filterableAttributes": ["age", "nb_paw_pads"], "sortableAttributes": ["iq"] })).await;
|
||||
index.wait_task(0).await;
|
||||
let (task, _) = index.get_task(0).await;
|
||||
dbg!(&task);
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
"uid": 0,
|
||||
"indexUid": "test",
|
||||
"status": "failed",
|
||||
"type": "settingsUpdate",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"rankingRules": [
|
||||
"custom"
|
||||
]
|
||||
},
|
||||
"error": {
|
||||
"message": "`custom` ranking rule is invalid. Valid ranking rules are words, typo, sort, proximity, attribute, exactness and custom ranking rules.",
|
||||
"code": "invalid_ranking_rule",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-ranking-rule"
|
||||
},
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
}
|
||||
"###);
|
||||
|
||||
index.update_settings(json!({ "displayedAttributes": ["doggos", "name"], "filterableAttributes": ["age", "nb_paw_pads"], "sortableAttributes": ["iq"] })).await;
|
||||
index.wait_task(1).await;
|
||||
let (task, _) = index.get_task(1).await;
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
"uid": 1,
|
||||
"indexUid": "test",
|
||||
"status": "succeeded",
|
||||
"type": "settingsUpdate",
|
||||
"canceledBy": null,
|
||||
|
Reference in New Issue
Block a user