mirror of
https://github.com/meilisearch/MeiliSearch
synced 2025-07-04 20:37:15 +02:00
Merge #3461
3461: Bring v1 changes into main r=curquiza a=Kerollmops Also bring back changes in milli (the remote repository) into main done during the pre-release Co-authored-by: Loïc Lecrenier <loic.lecrenier@me.com> Co-authored-by: bors[bot] <26634292+bors[bot]@users.noreply.github.com> Co-authored-by: curquiza <curquiza@users.noreply.github.com> Co-authored-by: Tamo <tamo@meilisearch.com> Co-authored-by: Philipp Ahlner <philipp@ahlner.com> Co-authored-by: Kerollmops <clement@meilisearch.com>
This commit is contained in:
commit
c88c3637b4
142 changed files with 7451 additions and 2398 deletions
|
@ -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 = "0.1.4"
|
||||
deserr = "0.3.0"
|
||||
dump = { path = "../dump" }
|
||||
either = "1.8.0"
|
||||
env_logger = "0.9.1"
|
||||
|
@ -55,7 +55,6 @@ rustls = "0.20.6"
|
|||
rustls-pemfile = "1.0.1"
|
||||
segment = { version = "0.2.1", optional = true }
|
||||
serde = { version = "1.0.145", features = ["derive"] }
|
||||
serde-cs = "0.2.4"
|
||||
serde_json = { version = "1.0.85", features = ["preserve_order"] }
|
||||
sha2 = "0.10.6"
|
||||
siphasher = "0.3.10"
|
||||
|
@ -74,6 +73,8 @@ walkdir = "2.3.2"
|
|||
yaup = "0.2.0"
|
||||
serde_urlencoded = "0.7.1"
|
||||
actix-utils = "3.0.1"
|
||||
atty = "0.2.14"
|
||||
termcolor = "1.1.3"
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = "2.7.0"
|
||||
|
|
|
@ -9,7 +9,7 @@ use actix_web::HttpRequest;
|
|||
use byte_unit::Byte;
|
||||
use http::header::CONTENT_TYPE;
|
||||
use index_scheduler::IndexScheduler;
|
||||
use meilisearch_auth::SearchRules;
|
||||
use meilisearch_auth::{AuthController, SearchRules};
|
||||
use meilisearch_types::InstanceUid;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
|
@ -82,7 +82,11 @@ pub struct SegmentAnalytics {
|
|||
}
|
||||
|
||||
impl SegmentAnalytics {
|
||||
pub async fn new(opt: &Opt, index_scheduler: Arc<IndexScheduler>) -> Arc<dyn Analytics> {
|
||||
pub async fn new(
|
||||
opt: &Opt,
|
||||
index_scheduler: Arc<IndexScheduler>,
|
||||
auth_controller: AuthController,
|
||||
) -> Arc<dyn Analytics> {
|
||||
let instance_uid = super::find_user_id(&opt.db_path);
|
||||
let first_time_run = instance_uid.is_none();
|
||||
let instance_uid = instance_uid.unwrap_or_else(|| Uuid::new_v4());
|
||||
|
@ -136,7 +140,7 @@ impl SegmentAnalytics {
|
|||
get_tasks_aggregator: TasksAggregator::default(),
|
||||
health_aggregator: HealthAggregator::default(),
|
||||
});
|
||||
tokio::spawn(segment.run(index_scheduler.clone()));
|
||||
tokio::spawn(segment.run(index_scheduler.clone(), auth_controller.clone()));
|
||||
|
||||
let this = Self { instance_uid, sender, user: user.clone() };
|
||||
|
||||
|
@ -361,7 +365,7 @@ impl Segment {
|
|||
})
|
||||
}
|
||||
|
||||
async fn run(mut self, index_scheduler: Arc<IndexScheduler>) {
|
||||
async fn run(mut self, index_scheduler: Arc<IndexScheduler>, auth_controller: AuthController) {
|
||||
const INTERVAL: Duration = Duration::from_secs(60 * 60); // one hour
|
||||
// The first batch must be sent after one hour.
|
||||
let mut interval =
|
||||
|
@ -370,7 +374,7 @@ impl Segment {
|
|||
loop {
|
||||
select! {
|
||||
_ = interval.tick() => {
|
||||
self.tick(index_scheduler.clone()).await;
|
||||
self.tick(index_scheduler.clone(), auth_controller.clone()).await;
|
||||
},
|
||||
msg = self.inbox.recv() => {
|
||||
match msg {
|
||||
|
@ -389,8 +393,14 @@ impl Segment {
|
|||
}
|
||||
}
|
||||
|
||||
async fn tick(&mut self, index_scheduler: Arc<IndexScheduler>) {
|
||||
if let Ok(stats) = create_all_stats(index_scheduler.into(), &SearchRules::default()) {
|
||||
async fn tick(
|
||||
&mut self,
|
||||
index_scheduler: Arc<IndexScheduler>,
|
||||
auth_controller: AuthController,
|
||||
) {
|
||||
if let Ok(stats) =
|
||||
create_all_stats(index_scheduler.into(), auth_controller, &SearchRules::default())
|
||||
{
|
||||
let _ = self
|
||||
.batcher
|
||||
.push(Identify {
|
||||
|
|
|
@ -2,7 +2,7 @@ use actix_web as aweb;
|
|||
use aweb::error::{JsonPayloadError, QueryPayloadError};
|
||||
use meilisearch_types::document_formats::{DocumentFormatError, PayloadType};
|
||||
use meilisearch_types::error::{Code, ErrorCode, ResponseError};
|
||||
use meilisearch_types::index_uid::IndexUidFormatError;
|
||||
use meilisearch_types::index_uid::{IndexUid, IndexUidFormatError};
|
||||
use serde_json::Value;
|
||||
use tokio::task::JoinError;
|
||||
|
||||
|
@ -24,10 +24,10 @@ pub enum MeilisearchHttpError {
|
|||
MissingPayload(PayloadType),
|
||||
#[error("The provided payload reached the size limit.")]
|
||||
PayloadTooLarge,
|
||||
#[error("Two indexes must be given for each swap. The list `{:?}` contains {} indexes.",
|
||||
.0, .0.len()
|
||||
#[error("Two indexes must be given for each swap. The list `[{}]` contains {} indexes.",
|
||||
.0.iter().map(|uid| format!("\"{uid}\"")).collect::<Vec<_>>().join(", "), .0.len()
|
||||
)]
|
||||
SwapIndexPayloadWrongLength(Vec<String>),
|
||||
SwapIndexPayloadWrongLength(Vec<IndexUid>),
|
||||
#[error(transparent)]
|
||||
IndexUid(#[from] IndexUidFormatError),
|
||||
#[error(transparent)]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::env;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -9,6 +10,7 @@ use index_scheduler::IndexScheduler;
|
|||
use meilisearch::analytics::Analytics;
|
||||
use meilisearch::{analytics, create_app, setup_meilisearch, Opt};
|
||||
use meilisearch_auth::{generate_master_key, AuthController, MASTER_KEY_MIN_SIZE};
|
||||
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||
|
@ -32,24 +34,19 @@ async fn main() -> anyhow::Result<()> {
|
|||
match (opt.env.as_ref(), &opt.master_key) {
|
||||
("production", Some(master_key)) if master_key.len() < MASTER_KEY_MIN_SIZE => {
|
||||
anyhow::bail!(
|
||||
"In production mode, the master key must be of at least {MASTER_KEY_MIN_SIZE} bytes, but the provided key is only {} bytes long
|
||||
"The master key must be at least {MASTER_KEY_MIN_SIZE} bytes in a production environment. The provided key is only {} bytes.
|
||||
|
||||
We generated a secure master key for you (you can safely copy this token):
|
||||
|
||||
>> export MEILI_MASTER_KEY={} <<",
|
||||
{}",
|
||||
master_key.len(),
|
||||
generate_master_key(),
|
||||
generated_master_key_message(),
|
||||
)
|
||||
}
|
||||
("production", None) => {
|
||||
anyhow::bail!(
|
||||
"In production mode, you must provide a master key to secure your instance. It can be specified via the MEILI_MASTER_KEY environment variable or the --master-key launch option.
|
||||
"You must provide a master key to secure your instance in a production environment. It can be specified via the MEILI_MASTER_KEY environment variable or the --master-key launch option.
|
||||
|
||||
We generated a secure master key for you (you can safely copy this token):
|
||||
|
||||
>> export MEILI_MASTER_KEY={} <<
|
||||
",
|
||||
generate_master_key()
|
||||
{}",
|
||||
generated_master_key_message()
|
||||
)
|
||||
}
|
||||
// No error; continue
|
||||
|
@ -60,7 +57,8 @@ We generated a secure master key for you (you can safely copy this token):
|
|||
|
||||
#[cfg(all(not(debug_assertions), feature = "analytics"))]
|
||||
let analytics = if !opt.no_analytics {
|
||||
analytics::SegmentAnalytics::new(&opt, index_scheduler.clone()).await
|
||||
analytics::SegmentAnalytics::new(&opt, index_scheduler.clone(), auth_controller.clone())
|
||||
.await
|
||||
} else {
|
||||
analytics::MockAnalytics::new(&opt)
|
||||
};
|
||||
|
@ -147,7 +145,7 @@ pub fn print_launch_resume(
|
|||
"
|
||||
Thank you for using Meilisearch!
|
||||
|
||||
We collect anonymized analytics to improve our product and your experience. To learn more, including how to turn off analytics, visit our dedicated documentation page: https://docs.meilisearch.com/learn/what_is_meilisearch/telemetry.html
|
||||
\nWe collect anonymized analytics to improve our product and your experience. To learn more, including how to turn off analytics, visit our dedicated documentation page: https://docs.meilisearch.com/learn/what_is_meilisearch/telemetry.html
|
||||
|
||||
Anonymous telemetry:\t\"Enabled\""
|
||||
);
|
||||
|
@ -170,16 +168,10 @@ Anonymous telemetry:\t\"Enabled\""
|
|||
eprintln!("A master key has been set. Requests to Meilisearch won't be authorized unless you provide an authentication key.");
|
||||
|
||||
if master_key.len() < MASTER_KEY_MIN_SIZE {
|
||||
eprintln!();
|
||||
log::warn!("The provided master key is too short (< {MASTER_KEY_MIN_SIZE} bytes)");
|
||||
eprintln!("A master key of at least {MASTER_KEY_MIN_SIZE} bytes will be required when switching to the production environment.");
|
||||
print_master_key_too_short_warning()
|
||||
}
|
||||
}
|
||||
("development", None) => {
|
||||
log::warn!("No master key found; The server will accept unidentified requests");
|
||||
eprintln!("If you need some protection in development mode, please export a key:\n\nexport MEILI_MASTER_KEY={}", generate_master_key());
|
||||
eprintln!("\nA master key of at least {MASTER_KEY_MIN_SIZE} bytes will be required when switching to the production environment.");
|
||||
}
|
||||
("development", None) => print_missing_master_key_warning(),
|
||||
// unreachable because Opt::try_build above would have failed already if any other value had been produced
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -190,3 +182,67 @@ Anonymous telemetry:\t\"Enabled\""
|
|||
eprintln!("Contact:\t\thttps://docs.meilisearch.com/resources/contact.html");
|
||||
eprintln!();
|
||||
}
|
||||
|
||||
const WARNING_BG_COLOR: Option<Color> = Some(Color::Ansi256(178));
|
||||
const WARNING_FG_COLOR: Option<Color> = Some(Color::Ansi256(0));
|
||||
|
||||
fn print_master_key_too_short_warning() {
|
||||
let choice =
|
||||
if atty::is(atty::Stream::Stderr) { ColorChoice::Auto } else { ColorChoice::Never };
|
||||
let mut stderr = StandardStream::stderr(choice);
|
||||
stderr
|
||||
.set_color(
|
||||
ColorSpec::new().set_bg(WARNING_BG_COLOR).set_fg(WARNING_FG_COLOR).set_bold(true),
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(stderr, "\n").unwrap();
|
||||
writeln!(
|
||||
stderr,
|
||||
" Meilisearch started with a master key considered unsafe for use in a production environment.
|
||||
|
||||
A master key of at least {MASTER_KEY_MIN_SIZE} bytes will be required when switching to a production environment."
|
||||
)
|
||||
.unwrap();
|
||||
stderr.reset().unwrap();
|
||||
writeln!(stderr).unwrap();
|
||||
|
||||
eprintln!("\n{}", generated_master_key_message());
|
||||
eprintln!(
|
||||
"\nRestart Meilisearch with the argument above to use this new and secure master key."
|
||||
)
|
||||
}
|
||||
|
||||
fn print_missing_master_key_warning() {
|
||||
let choice =
|
||||
if atty::is(atty::Stream::Stderr) { ColorChoice::Auto } else { ColorChoice::Never };
|
||||
let mut stderr = StandardStream::stderr(choice);
|
||||
stderr
|
||||
.set_color(
|
||||
ColorSpec::new().set_bg(WARNING_BG_COLOR).set_fg(WARNING_FG_COLOR).set_bold(true),
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(stderr, "\n").unwrap();
|
||||
writeln!(
|
||||
stderr,
|
||||
" No master key was found. The server will accept unidentified requests.
|
||||
|
||||
A master key of at least {MASTER_KEY_MIN_SIZE} bytes will be required when switching to a production environment."
|
||||
)
|
||||
.unwrap();
|
||||
stderr.reset().unwrap();
|
||||
writeln!(stderr).unwrap();
|
||||
|
||||
eprintln!("\n{}", generated_master_key_message());
|
||||
eprintln!(
|
||||
"\nRestart Meilisearch with the argument above to use this new and secure master key."
|
||||
)
|
||||
}
|
||||
|
||||
fn generated_master_key_message() -> String {
|
||||
format!(
|
||||
"We generated a new secure master key for you (you can safely use this token):
|
||||
|
||||
>> --master-key {} <<",
|
||||
generate_master_key()
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,14 +4,15 @@ use actix_web::{web, HttpRequest, HttpResponse};
|
|||
use deserr::DeserializeFromValue;
|
||||
use meilisearch_auth::error::AuthControllerError;
|
||||
use meilisearch_auth::AuthController;
|
||||
use meilisearch_types::deserr::query_params::Param;
|
||||
use meilisearch_types::deserr::{DeserrJsonError, DeserrQueryParamError};
|
||||
use meilisearch_types::error::deserr_codes::*;
|
||||
use meilisearch_types::error::{Code, DeserrError, ResponseError, TakeErrorMessage};
|
||||
use meilisearch_types::error::{Code, ResponseError};
|
||||
use meilisearch_types::keys::{Action, CreateApiKey, Key, PatchApiKey};
|
||||
use serde::{Deserialize, Serialize};
|
||||
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;
|
||||
|
@ -36,7 +37,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
|||
|
||||
pub async fn create_api_key(
|
||||
auth_controller: GuardedData<ActionPolicy<{ actions::KEYS_CREATE }>, AuthController>,
|
||||
body: ValidatedJson<CreateApiKey, DeserrError>,
|
||||
body: ValidatedJson<CreateApiKey, DeserrJsonError>,
|
||||
_req: HttpRequest,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let v = body.into_inner();
|
||||
|
@ -50,26 +51,23 @@ pub async fn create_api_key(
|
|||
Ok(HttpResponse::Created().json(res))
|
||||
}
|
||||
|
||||
#[derive(DeserializeFromValue, Deserialize, Debug, Clone, Copy)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
#[derive(DeserializeFromValue, Debug, Clone, Copy)]
|
||||
#[deserr(error = DeserrQueryParamError, 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,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidApiKeyOffset>)]
|
||||
pub offset: Param<usize>,
|
||||
#[deserr(default = Param(PAGINATION_DEFAULT_LIMIT), error = DeserrQueryParamError<InvalidApiKeyLimit>)]
|
||||
pub limit: Param<usize>,
|
||||
}
|
||||
impl ListApiKeys {
|
||||
fn as_pagination(self) -> Pagination {
|
||||
Pagination { offset: self.offset, limit: self.limit }
|
||||
Pagination { offset: self.offset.0, limit: self.limit.0 }
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_api_keys(
|
||||
auth_controller: GuardedData<ActionPolicy<{ actions::KEYS_GET }>, AuthController>,
|
||||
list_api_keys: QueryParameter<ListApiKeys, DeserrError>,
|
||||
list_api_keys: QueryParameter<ListApiKeys, DeserrQueryParamError>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let paginate = list_api_keys.into_inner().as_pagination();
|
||||
let page_view = tokio::task::spawn_blocking(move || -> Result<_, AuthControllerError> {
|
||||
|
@ -106,7 +104,7 @@ pub async fn get_api_key(
|
|||
|
||||
pub async fn patch_api_key(
|
||||
auth_controller: GuardedData<ActionPolicy<{ actions::KEYS_UPDATE }>, AuthController>,
|
||||
body: ValidatedJson<PatchApiKey, DeserrError>,
|
||||
body: ValidatedJson<PatchApiKey, DeserrJsonError>,
|
||||
path: web::Path<AuthParam>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let key = path.into_inner().key;
|
||||
|
@ -172,7 +170,7 @@ impl KeyView {
|
|||
key: generated_key,
|
||||
uid: key.uid,
|
||||
actions: key.actions,
|
||||
indexes: key.indexes.into_iter().map(String::from).collect(),
|
||||
indexes: key.indexes.into_iter().map(|x| x.to_string()).collect(),
|
||||
expires_at: key.expires_at,
|
||||
created_at: key.created_at,
|
||||
updated_at: key.updated_at,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use std::io::ErrorKind;
|
||||
use std::num::ParseIntError;
|
||||
|
||||
use actix_web::http::header::CONTENT_TYPE;
|
||||
use actix_web::web::Data;
|
||||
|
@ -9,25 +8,25 @@ use deserr::DeserializeFromValue;
|
|||
use futures::StreamExt;
|
||||
use index_scheduler::IndexScheduler;
|
||||
use log::debug;
|
||||
use meilisearch_types::deserr::query_params::Param;
|
||||
use meilisearch_types::deserr::{DeserrJsonError, DeserrQueryParamError};
|
||||
use meilisearch_types::document_formats::{read_csv, read_json, read_ndjson, PayloadType};
|
||||
use meilisearch_types::error::deserr_codes::*;
|
||||
use meilisearch_types::error::{DeserrError, ResponseError, TakeErrorMessage};
|
||||
use meilisearch_types::error::ResponseError;
|
||||
use meilisearch_types::heed::RoTxn;
|
||||
use meilisearch_types::index_uid::IndexUid;
|
||||
use meilisearch_types::milli::update::IndexDocumentsMethod;
|
||||
use meilisearch_types::star_or::StarOr;
|
||||
use meilisearch_types::star_or::OptionStarOrList;
|
||||
use meilisearch_types::tasks::KindWithContent;
|
||||
use meilisearch_types::{milli, Document, Index};
|
||||
use mime::Mime;
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::Deserialize;
|
||||
use serde_cs::vec::CS;
|
||||
use serde_json::Value;
|
||||
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;
|
||||
|
@ -36,7 +35,7 @@ use crate::extractors::authentication::GuardedData;
|
|||
use crate::extractors::payload::Payload;
|
||||
use crate::extractors::query_parameters::QueryParameter;
|
||||
use crate::extractors::sequential_extractor::SeqHandler;
|
||||
use crate::routes::{fold_star_or, PaginationView, SummarizedTaskView};
|
||||
use crate::routes::{PaginationView, SummarizedTaskView, PAGINATION_DEFAULT_LIMIT};
|
||||
|
||||
static ACCEPTED_CONTENT_TYPE: Lazy<Vec<String>> = Lazy::new(|| {
|
||||
vec!["application/json".to_string(), "application/x-ndjson".to_string(), "text/csv".to_string()]
|
||||
|
@ -81,23 +80,26 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
|||
);
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, DeserializeFromValue)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
#[derive(Debug, DeserializeFromValue)]
|
||||
#[deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields)]
|
||||
pub struct GetDocument {
|
||||
#[deserr(error = DeserrError<InvalidDocumentFields>)]
|
||||
fields: Option<CS<StarOr<String>>>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidDocumentFields>)]
|
||||
fields: OptionStarOrList<String>,
|
||||
}
|
||||
|
||||
pub async fn get_document(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_GET }>, Data<IndexScheduler>>,
|
||||
path: web::Path<DocumentParam>,
|
||||
params: QueryParameter<GetDocument, DeserrError>,
|
||||
document_param: web::Path<DocumentParam>,
|
||||
params: QueryParameter<GetDocument, DeserrQueryParamError>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let GetDocument { fields } = params.into_inner();
|
||||
let attributes_to_retrieve = fields.and_then(fold_star_or);
|
||||
let DocumentParam { index_uid, document_id } = document_param.into_inner();
|
||||
let index_uid = IndexUid::try_from(index_uid)?;
|
||||
|
||||
let index = index_scheduler.index(&path.index_uid)?;
|
||||
let document = retrieve_document(&index, &path.document_id, attributes_to_retrieve)?;
|
||||
let GetDocument { fields } = params.into_inner();
|
||||
let attributes_to_retrieve = fields.merge_star_and_none();
|
||||
|
||||
let index = index_scheduler.index(&index_uid)?;
|
||||
let document = retrieve_document(&index, &document_id, attributes_to_retrieve)?;
|
||||
debug!("returns: {:?}", document);
|
||||
Ok(HttpResponse::Ok().json(document))
|
||||
}
|
||||
|
@ -108,60 +110,68 @@ pub async fn delete_document(
|
|||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let DocumentParam { index_uid, document_id } = path.into_inner();
|
||||
let index_uid = IndexUid::try_from(index_uid)?;
|
||||
|
||||
analytics.delete_documents(DocumentDeletionKind::PerDocumentId, &req);
|
||||
|
||||
let DocumentParam { document_id, index_uid } = path.into_inner();
|
||||
let task = KindWithContent::DocumentDeletion { index_uid, documents_ids: vec![document_id] };
|
||||
let task = KindWithContent::DocumentDeletion {
|
||||
index_uid: index_uid.to_string(),
|
||||
documents_ids: vec![document_id],
|
||||
};
|
||||
let task: SummarizedTaskView =
|
||||
tokio::task::spawn_blocking(move || index_scheduler.register(task)).await??.into();
|
||||
debug!("returns: {:?}", task);
|
||||
Ok(HttpResponse::Accepted().json(task))
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, DeserializeFromValue)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
#[derive(Debug, DeserializeFromValue)]
|
||||
#[deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields)]
|
||||
pub struct BrowseQuery {
|
||||
#[deserr(error = DeserrError<InvalidDocumentFields>, default, from(&String) = parse_usize_take_error_message -> TakeErrorMessage<ParseIntError>)]
|
||||
offset: usize,
|
||||
#[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>>>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidDocumentOffset>)]
|
||||
offset: Param<usize>,
|
||||
#[deserr(default = Param(PAGINATION_DEFAULT_LIMIT), error = DeserrQueryParamError<InvalidDocumentLimit>)]
|
||||
limit: Param<usize>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidDocumentFields>)]
|
||||
fields: OptionStarOrList<String>,
|
||||
}
|
||||
|
||||
pub async fn get_all_documents(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_GET }>, Data<IndexScheduler>>,
|
||||
index_uid: web::Path<String>,
|
||||
params: QueryParameter<BrowseQuery, DeserrError>,
|
||||
params: QueryParameter<BrowseQuery, DeserrQueryParamError>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
debug!("called with params: {:?}", params);
|
||||
let BrowseQuery { limit, offset, fields } = params.into_inner();
|
||||
let attributes_to_retrieve = fields.and_then(fold_star_or);
|
||||
let attributes_to_retrieve = fields.merge_star_and_none();
|
||||
|
||||
let index = index_scheduler.index(&index_uid)?;
|
||||
let (total, documents) = retrieve_documents(&index, offset, limit, attributes_to_retrieve)?;
|
||||
let (total, documents) = retrieve_documents(&index, offset.0, limit.0, attributes_to_retrieve)?;
|
||||
|
||||
let ret = PaginationView::new(offset, limit, total as usize, documents);
|
||||
let ret = PaginationView::new(offset.0, limit.0, total as usize, documents);
|
||||
|
||||
debug!("returns: {:?}", ret);
|
||||
Ok(HttpResponse::Ok().json(ret))
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, DeserializeFromValue)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
#[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)]
|
||||
pub struct UpdateDocumentsQuery {
|
||||
#[deserr(error = DeserrError<InvalidIndexPrimaryKey>)]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidIndexPrimaryKey>)]
|
||||
pub primary_key: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn add_documents(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_ADD }>, Data<IndexScheduler>>,
|
||||
index_uid: web::Path<String>,
|
||||
params: QueryParameter<UpdateDocumentsQuery, DeserrError>,
|
||||
params: QueryParameter<UpdateDocumentsQuery, DeserrJsonError>,
|
||||
body: Payload,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
|
||||
debug!("called with params: {:?}", params);
|
||||
let params = params.into_inner();
|
||||
|
||||
|
@ -171,7 +181,7 @@ pub async fn add_documents(
|
|||
let task = document_addition(
|
||||
extract_mime_type(&req)?,
|
||||
index_scheduler,
|
||||
index_uid.into_inner(),
|
||||
index_uid,
|
||||
params.primary_key,
|
||||
body,
|
||||
IndexDocumentsMethod::ReplaceDocuments,
|
||||
|
@ -184,14 +194,15 @@ 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, DeserrError>,
|
||||
index_uid: web::Path<String>,
|
||||
params: QueryParameter<UpdateDocumentsQuery, DeserrJsonError>,
|
||||
body: Payload,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
|
||||
debug!("called with params: {:?}", params);
|
||||
let index_uid = path.into_inner();
|
||||
|
||||
analytics.update_documents(¶ms, index_scheduler.index(&index_uid).is_err(), &req);
|
||||
|
||||
|
@ -213,7 +224,7 @@ pub async fn update_documents(
|
|||
async fn document_addition(
|
||||
mime_type: Option<Mime>,
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_ADD }>, Data<IndexScheduler>>,
|
||||
index_uid: String,
|
||||
index_uid: IndexUid,
|
||||
primary_key: Option<String>,
|
||||
mut body: Payload,
|
||||
method: IndexDocumentsMethod,
|
||||
|
@ -234,9 +245,6 @@ async fn document_addition(
|
|||
}
|
||||
};
|
||||
|
||||
// is your indexUid valid?
|
||||
let index_uid = IndexUid::try_from(index_uid)?.into_inner();
|
||||
|
||||
let (uuid, mut update_file) = index_scheduler.create_update_file()?;
|
||||
|
||||
let temp_file = match tempfile() {
|
||||
|
@ -312,7 +320,7 @@ async fn document_addition(
|
|||
documents_count,
|
||||
primary_key,
|
||||
allow_index_creation,
|
||||
index_uid,
|
||||
index_uid: index_uid.to_string(),
|
||||
};
|
||||
|
||||
let scheduler = index_scheduler.clone();
|
||||
|
@ -330,12 +338,13 @@ async fn document_addition(
|
|||
|
||||
pub async fn delete_documents(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_DELETE }>, Data<IndexScheduler>>,
|
||||
path: web::Path<String>,
|
||||
index_uid: web::Path<String>,
|
||||
body: web::Json<Vec<Value>>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
debug!("called with params: {:?}", body);
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
|
||||
analytics.delete_documents(DocumentDeletionKind::PerBatch, &req);
|
||||
|
||||
|
@ -345,7 +354,7 @@ pub async fn delete_documents(
|
|||
.collect();
|
||||
|
||||
let task =
|
||||
KindWithContent::DocumentDeletion { index_uid: path.into_inner(), documents_ids: ids };
|
||||
KindWithContent::DocumentDeletion { index_uid: index_uid.to_string(), documents_ids: ids };
|
||||
let task: SummarizedTaskView =
|
||||
tokio::task::spawn_blocking(move || index_scheduler.register(task)).await??.into();
|
||||
|
||||
|
@ -355,13 +364,14 @@ pub async fn delete_documents(
|
|||
|
||||
pub async fn clear_all_documents(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_DELETE }>, Data<IndexScheduler>>,
|
||||
path: web::Path<String>,
|
||||
index_uid: web::Path<String>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
analytics.delete_documents(DocumentDeletionKind::ClearAll, &req);
|
||||
|
||||
let task = KindWithContent::DocumentClear { index_uid: path.into_inner() };
|
||||
let task = KindWithContent::DocumentClear { index_uid: index_uid.to_string() };
|
||||
let task: SummarizedTaskView =
|
||||
tokio::task::spawn_blocking(move || index_scheduler.register(task)).await??.into();
|
||||
|
||||
|
|
|
@ -5,16 +5,18 @@ use actix_web::{web, HttpRequest, HttpResponse};
|
|||
use deserr::{DeserializeError, DeserializeFromValue, ValuePointerRef};
|
||||
use index_scheduler::IndexScheduler;
|
||||
use log::debug;
|
||||
use meilisearch_types::deserr::error_messages::immutable_field_error;
|
||||
use meilisearch_types::deserr::query_params::Param;
|
||||
use meilisearch_types::deserr::{DeserrJsonError, DeserrQueryParamError};
|
||||
use meilisearch_types::error::deserr_codes::*;
|
||||
use meilisearch_types::error::{unwrap_any, Code, DeserrError, ResponseError, TakeErrorMessage};
|
||||
use meilisearch_types::error::{unwrap_any, Code, ResponseError};
|
||||
use meilisearch_types::index_uid::IndexUid;
|
||||
use meilisearch_types::milli::{self, FieldDistribution, Index};
|
||||
use meilisearch_types::tasks::KindWithContent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::Serialize;
|
||||
use serde_json::json;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use self::search::parse_usize_take_error_message;
|
||||
use super::{Pagination, SummarizedTaskView, PAGINATION_DEFAULT_LIMIT};
|
||||
use crate::analytics::Analytics;
|
||||
use crate::extractors::authentication::policies::*;
|
||||
|
@ -48,7 +50,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
|||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Serialize, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IndexView {
|
||||
pub uid: String,
|
||||
|
@ -71,26 +73,23 @@ impl IndexView {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(DeserializeFromValue, Deserialize, Debug, Clone, Copy)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
#[derive(DeserializeFromValue, Debug, Clone, Copy)]
|
||||
#[deserr(error = DeserrQueryParamError, 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,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidIndexOffset>)]
|
||||
pub offset: Param<usize>,
|
||||
#[deserr(default = Param(PAGINATION_DEFAULT_LIMIT), error = DeserrQueryParamError<InvalidIndexLimit>)]
|
||||
pub limit: Param<usize>,
|
||||
}
|
||||
impl ListIndexes {
|
||||
fn as_pagination(self) -> Pagination {
|
||||
Pagination { offset: self.offset, limit: self.limit }
|
||||
Pagination { offset: self.offset.0, limit: self.limit.0 }
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list_indexes(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_GET }>, Data<IndexScheduler>>,
|
||||
paginate: QueryParameter<ListIndexes, DeserrError>,
|
||||
paginate: QueryParameter<ListIndexes, DeserrQueryParamError>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let search_rules = &index_scheduler.filters().search_rules;
|
||||
let indexes: Vec<_> = index_scheduler.indexes()?;
|
||||
|
@ -107,22 +106,21 @@ pub async fn list_indexes(
|
|||
}
|
||||
|
||||
#[derive(DeserializeFromValue, Debug)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
#[deserr(error = DeserrJsonError, 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>)]
|
||||
#[deserr(error = DeserrJsonError<InvalidIndexUid>, missing_field_error = DeserrJsonError::missing_index_uid)]
|
||||
uid: IndexUid,
|
||||
#[deserr(default, error = DeserrJsonError<InvalidIndexPrimaryKey>)]
|
||||
primary_key: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn create_index(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_CREATE }>, Data<IndexScheduler>>,
|
||||
body: ValidatedJson<IndexCreateRequest, DeserrError>,
|
||||
body: ValidatedJson<IndexCreateRequest, DeserrJsonError>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let IndexCreateRequest { primary_key, uid } = body.into_inner();
|
||||
let uid = IndexUid::try_from(uid)?.into_inner();
|
||||
|
||||
let allow_index_creation = index_scheduler.filters().search_rules.is_index_authorized(&uid);
|
||||
if allow_index_creation {
|
||||
|
@ -132,7 +130,7 @@ pub async fn create_index(
|
|||
Some(&req),
|
||||
);
|
||||
|
||||
let task = KindWithContent::IndexCreation { index_uid: uid, primary_key };
|
||||
let task = KindWithContent::IndexCreation { index_uid: uid.to_string(), primary_key };
|
||||
let task: SummarizedTaskView =
|
||||
tokio::task::spawn_blocking(move || index_scheduler.register(task)).await??.into();
|
||||
|
||||
|
@ -146,25 +144,23 @@ 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
|
||||
) -> DeserrJsonError {
|
||||
match field {
|
||||
"uid" => immutable_field_error(field, accepted, Code::ImmutableIndexUid),
|
||||
"createdAt" => immutable_field_error(field, accepted, Code::ImmutableIndexCreatedAt),
|
||||
"updatedAt" => immutable_field_error(field, accepted, Code::ImmutableIndexUpdatedAt),
|
||||
_ => unwrap_any(DeserrJsonError::<BadRequest>::error::<Infallible>(
|
||||
None,
|
||||
deserr::ErrorKind::UnknownKey { key: field, accepted },
|
||||
location,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeserializeFromValue, Debug)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields = deny_immutable_fields_index)]
|
||||
#[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields = deny_immutable_fields_index)]
|
||||
pub struct UpdateIndexRequest {
|
||||
#[deserr(error = DeserrError<InvalidIndexPrimaryKey>)]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidIndexPrimaryKey>)]
|
||||
primary_key: Option<String>,
|
||||
}
|
||||
|
||||
|
@ -172,6 +168,8 @@ pub async fn get_index(
|
|||
index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_GET }>, Data<IndexScheduler>>,
|
||||
index_uid: web::Path<String>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
|
||||
let index = index_scheduler.index(&index_uid)?;
|
||||
let index_view = IndexView::new(index_uid.into_inner(), &index)?;
|
||||
|
||||
|
@ -182,12 +180,13 @@ 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, DeserrError>,
|
||||
index_uid: web::Path<String>,
|
||||
body: ValidatedJson<UpdateIndexRequest, DeserrJsonError>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
debug!("called with params: {:?}", body);
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
let body = body.into_inner();
|
||||
analytics.publish(
|
||||
"Index Updated".to_string(),
|
||||
|
@ -196,7 +195,7 @@ pub async fn update_index(
|
|||
);
|
||||
|
||||
let task = KindWithContent::IndexUpdate {
|
||||
index_uid: path.into_inner(),
|
||||
index_uid: index_uid.into_inner(),
|
||||
primary_key: body.primary_key,
|
||||
};
|
||||
|
||||
|
@ -211,6 +210,7 @@ pub async fn delete_index(
|
|||
index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_DELETE }>, Data<IndexScheduler>>,
|
||||
index_uid: web::Path<String>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
let task = KindWithContent::IndexDeletion { index_uid: index_uid.into_inner() };
|
||||
let task: SummarizedTaskView =
|
||||
tokio::task::spawn_blocking(move || index_scheduler.register(task)).await??.into();
|
||||
|
@ -224,6 +224,7 @@ pub async fn get_index_stats(
|
|||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
analytics.publish("Stats Seen".to_string(), json!({ "per_index_uid": true }), Some(&req));
|
||||
|
||||
let stats = IndexStats::new((*index_scheduler).clone(), index_uid.into_inner())?;
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use index_scheduler::IndexScheduler;
|
||||
use log::debug;
|
||||
use meilisearch_auth::IndexSearchRules;
|
||||
use meilisearch_types::deserr::query_params::Param;
|
||||
use meilisearch_types::deserr::{DeserrJsonError, DeserrQueryParamError};
|
||||
use meilisearch_types::error::deserr_codes::*;
|
||||
use meilisearch_types::error::{DeserrError, ResponseError, TakeErrorMessage};
|
||||
use serde_cs::vec::CS;
|
||||
use meilisearch_types::error::ResponseError;
|
||||
use meilisearch_types::index_uid::IndexUid;
|
||||
use meilisearch_types::serde_cs::vec::CS;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::analytics::{Analytics, SearchAggregator};
|
||||
|
@ -16,7 +17,6 @@ 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_take_error_message;
|
||||
use crate::search::{
|
||||
perform_search, MatchingStrategy, SearchQuery, DEFAULT_CROP_LENGTH, DEFAULT_CROP_MARKER,
|
||||
DEFAULT_HIGHLIGHT_POST_TAG, DEFAULT_HIGHLIGHT_PRE_TAG, DEFAULT_SEARCH_LIMIT,
|
||||
|
@ -31,54 +31,42 @@ 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(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
#[deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields)]
|
||||
pub struct SearchQueryGet {
|
||||
#[deserr(error = DeserrError<InvalidSearchQ>)]
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchQ>)]
|
||||
q: Option<String>,
|
||||
#[deserr(error = DeserrError<InvalidSearchOffset>, default = DEFAULT_SEARCH_OFFSET(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
|
||||
offset: usize,
|
||||
#[deserr(error = DeserrError<InvalidSearchLimit>, default = DEFAULT_SEARCH_LIMIT(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
|
||||
limit: usize,
|
||||
#[deserr(error = DeserrError<InvalidSearchPage>, from(&String) = from_string_to_option_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
|
||||
page: Option<usize>,
|
||||
#[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>)]
|
||||
#[deserr(default = Param(DEFAULT_SEARCH_OFFSET()), error = DeserrQueryParamError<InvalidSearchOffset>)]
|
||||
offset: Param<usize>,
|
||||
#[deserr(default = Param(DEFAULT_SEARCH_LIMIT()), error = DeserrQueryParamError<InvalidSearchLimit>)]
|
||||
limit: Param<usize>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchPage>)]
|
||||
page: Option<Param<usize>>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchHitsPerPage>)]
|
||||
hits_per_page: Option<Param<usize>>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchAttributesToRetrieve>)]
|
||||
attributes_to_retrieve: Option<CS<String>>,
|
||||
#[deserr(error = DeserrError<InvalidSearchAttributesToCrop>)]
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchAttributesToCrop>)]
|
||||
attributes_to_crop: Option<CS<String>>,
|
||||
#[deserr(error = DeserrError<InvalidSearchCropLength>, default = DEFAULT_CROP_LENGTH(), from(&String) = parse_usize_take_error_message -> TakeErrorMessage<std::num::ParseIntError>)]
|
||||
crop_length: usize,
|
||||
#[deserr(error = DeserrError<InvalidSearchAttributesToHighlight>)]
|
||||
#[deserr(default = Param(DEFAULT_CROP_LENGTH()), error = DeserrQueryParamError<InvalidSearchCropLength>)]
|
||||
crop_length: Param<usize>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchAttributesToHighlight>)]
|
||||
attributes_to_highlight: Option<CS<String>>,
|
||||
#[deserr(error = DeserrError<InvalidSearchFilter>)]
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchFilter>)]
|
||||
filter: Option<String>,
|
||||
#[deserr(error = DeserrError<InvalidSearchSort>)]
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchSort>)]
|
||||
sort: Option<String>,
|
||||
#[deserr(error = DeserrError<InvalidSearchShowMatchesPosition>, default, from(&String) = parse_bool_take_error_message -> TakeErrorMessage<std::str::ParseBoolError>)]
|
||||
show_matches_position: bool,
|
||||
#[deserr(error = DeserrError<InvalidSearchFacets>)]
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchShowMatchesPosition>)]
|
||||
show_matches_position: Param<bool>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchFacets>)]
|
||||
facets: Option<CS<String>>,
|
||||
#[deserr(error = DeserrError<InvalidSearchHighlightPreTag>, default = DEFAULT_HIGHLIGHT_PRE_TAG())]
|
||||
#[deserr( default = DEFAULT_HIGHLIGHT_PRE_TAG(), error = DeserrQueryParamError<InvalidSearchHighlightPreTag>)]
|
||||
highlight_pre_tag: String,
|
||||
#[deserr(error = DeserrError<InvalidSearchHighlightPostTag>, default = DEFAULT_HIGHLIGHT_POST_TAG())]
|
||||
#[deserr( default = DEFAULT_HIGHLIGHT_POST_TAG(), error = DeserrQueryParamError<InvalidSearchHighlightPostTag>)]
|
||||
highlight_post_tag: String,
|
||||
#[deserr(error = DeserrError<InvalidSearchCropMarker>, default = DEFAULT_CROP_MARKER())]
|
||||
#[deserr(default = DEFAULT_CROP_MARKER(), error = DeserrQueryParamError<InvalidSearchCropMarker>)]
|
||||
crop_marker: String,
|
||||
#[deserr(error = DeserrError<InvalidSearchMatchingStrategy>, default)]
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidSearchMatchingStrategy>)]
|
||||
matching_strategy: MatchingStrategy,
|
||||
}
|
||||
|
||||
|
@ -94,17 +82,17 @@ impl From<SearchQueryGet> for SearchQuery {
|
|||
|
||||
Self {
|
||||
q: other.q,
|
||||
offset: other.offset,
|
||||
limit: other.limit,
|
||||
page: other.page,
|
||||
hits_per_page: other.hits_per_page,
|
||||
offset: other.offset.0,
|
||||
limit: other.limit.0,
|
||||
page: other.page.as_deref().copied(),
|
||||
hits_per_page: other.hits_per_page.as_deref().copied(),
|
||||
attributes_to_retrieve: other.attributes_to_retrieve.map(|o| o.into_iter().collect()),
|
||||
attributes_to_crop: other.attributes_to_crop.map(|o| o.into_iter().collect()),
|
||||
crop_length: other.crop_length,
|
||||
crop_length: other.crop_length.0,
|
||||
attributes_to_highlight: other.attributes_to_highlight.map(|o| o.into_iter().collect()),
|
||||
filter,
|
||||
sort: other.sort.map(|attr| fix_sort_query_parameters(&attr)),
|
||||
show_matches_position: other.show_matches_position,
|
||||
show_matches_position: other.show_matches_position.0,
|
||||
facets: other.facets.map(|o| o.into_iter().collect()),
|
||||
highlight_pre_tag: other.highlight_pre_tag,
|
||||
highlight_post_tag: other.highlight_post_tag,
|
||||
|
@ -162,11 +150,13 @@ 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, DeserrError>,
|
||||
params: QueryParameter<SearchQueryGet, DeserrQueryParamError>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
debug!("called with params: {:?}", params);
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
|
||||
let mut query: SearchQuery = params.into_inner().into();
|
||||
|
||||
// Tenant token search_rules.
|
||||
|
@ -194,10 +184,12 @@ 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, DeserrError>,
|
||||
params: ValidatedJson<SearchQuery, DeserrJsonError>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
|
||||
let mut query = params.into_inner();
|
||||
debug!("search called with params: {:?}", query);
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ use actix_web::web::Data;
|
|||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use index_scheduler::IndexScheduler;
|
||||
use log::debug;
|
||||
use meilisearch_types::error::{DeserrError, ResponseError};
|
||||
use meilisearch_types::deserr::DeserrJsonError;
|
||||
use meilisearch_types::error::ResponseError;
|
||||
use meilisearch_types::index_uid::IndexUid;
|
||||
use meilisearch_types::settings::{settings, RankingRuleView, Settings, Unchecked};
|
||||
use meilisearch_types::tasks::KindWithContent;
|
||||
|
@ -40,12 +41,14 @@ macro_rules! make_setting_route {
|
|||
>,
|
||||
index_uid: web::Path<String>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
|
||||
let new_settings = Settings { $attr: Setting::Reset.into(), ..Default::default() };
|
||||
|
||||
let allow_index_creation = index_scheduler.filters().allow_index_creation;
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?.into_inner();
|
||||
|
||||
let task = KindWithContent::SettingsUpdate {
|
||||
index_uid,
|
||||
index_uid: index_uid.to_string(),
|
||||
new_settings: Box::new(new_settings),
|
||||
is_deletion: true,
|
||||
allow_index_creation,
|
||||
|
@ -69,6 +72,8 @@ macro_rules! make_setting_route {
|
|||
req: HttpRequest,
|
||||
$analytics_var: web::Data<dyn Analytics>,
|
||||
) -> std::result::Result<HttpResponse, ResponseError> {
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
|
||||
let body = body.into_inner();
|
||||
|
||||
$analytics(&body, &req);
|
||||
|
@ -82,9 +87,9 @@ macro_rules! make_setting_route {
|
|||
};
|
||||
|
||||
let allow_index_creation = index_scheduler.filters().allow_index_creation;
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?.into_inner();
|
||||
|
||||
let task = KindWithContent::SettingsUpdate {
|
||||
index_uid,
|
||||
index_uid: index_uid.to_string(),
|
||||
new_settings: Box::new(new_settings),
|
||||
is_deletion: false,
|
||||
allow_index_creation,
|
||||
|
@ -105,6 +110,8 @@ macro_rules! make_setting_route {
|
|||
>,
|
||||
index_uid: actix_web::web::Path<String>,
|
||||
) -> std::result::Result<HttpResponse, ResponseError> {
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
|
||||
let index = index_scheduler.index(&index_uid)?;
|
||||
let rtxn = index.read_txn()?;
|
||||
let settings = settings(&index, &rtxn)?;
|
||||
|
@ -130,7 +137,7 @@ make_setting_route!(
|
|||
"/filterable-attributes",
|
||||
put,
|
||||
std::collections::BTreeSet<String>,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::deserr::DeserrJsonError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsFilterableAttributes,
|
||||
>,
|
||||
filterable_attributes,
|
||||
|
@ -156,7 +163,7 @@ make_setting_route!(
|
|||
"/sortable-attributes",
|
||||
put,
|
||||
std::collections::BTreeSet<String>,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::deserr::DeserrJsonError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsSortableAttributes,
|
||||
>,
|
||||
sortable_attributes,
|
||||
|
@ -182,7 +189,7 @@ make_setting_route!(
|
|||
"/displayed-attributes",
|
||||
put,
|
||||
Vec<String>,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::deserr::DeserrJsonError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsDisplayedAttributes,
|
||||
>,
|
||||
displayed_attributes,
|
||||
|
@ -208,7 +215,7 @@ make_setting_route!(
|
|||
"/typo-tolerance",
|
||||
patch,
|
||||
meilisearch_types::settings::TypoSettings,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::deserr::DeserrJsonError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsTypoTolerance,
|
||||
>,
|
||||
typo_tolerance,
|
||||
|
@ -253,7 +260,7 @@ make_setting_route!(
|
|||
"/searchable-attributes",
|
||||
put,
|
||||
Vec<String>,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::deserr::DeserrJsonError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsSearchableAttributes,
|
||||
>,
|
||||
searchable_attributes,
|
||||
|
@ -279,7 +286,7 @@ make_setting_route!(
|
|||
"/stop-words",
|
||||
put,
|
||||
std::collections::BTreeSet<String>,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::deserr::DeserrJsonError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsStopWords,
|
||||
>,
|
||||
stop_words,
|
||||
|
@ -304,7 +311,7 @@ make_setting_route!(
|
|||
"/synonyms",
|
||||
put,
|
||||
std::collections::BTreeMap<String, Vec<String>>,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::deserr::DeserrJsonError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsSynonyms,
|
||||
>,
|
||||
synonyms,
|
||||
|
@ -329,7 +336,7 @@ make_setting_route!(
|
|||
"/distinct-attribute",
|
||||
put,
|
||||
String,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::deserr::DeserrJsonError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsDistinctAttribute,
|
||||
>,
|
||||
distinct_attribute,
|
||||
|
@ -353,7 +360,7 @@ make_setting_route!(
|
|||
"/ranking-rules",
|
||||
put,
|
||||
Vec<meilisearch_types::settings::RankingRuleView>,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::deserr::DeserrJsonError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsRankingRules,
|
||||
>,
|
||||
ranking_rules,
|
||||
|
@ -384,7 +391,7 @@ make_setting_route!(
|
|||
"/faceting",
|
||||
patch,
|
||||
meilisearch_types::settings::FacetingSettings,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::deserr::DeserrJsonError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsFaceting,
|
||||
>,
|
||||
faceting,
|
||||
|
@ -409,7 +416,7 @@ make_setting_route!(
|
|||
"/pagination",
|
||||
patch,
|
||||
meilisearch_types::settings::PaginationSettings,
|
||||
meilisearch_types::error::DeserrError<
|
||||
meilisearch_types::deserr::DeserrJsonError<
|
||||
meilisearch_types::error::deserr_codes::InvalidSettingsPagination,
|
||||
>,
|
||||
pagination,
|
||||
|
@ -461,10 +468,12 @@ generate_configure!(
|
|||
pub async fn update_all(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::SETTINGS_UPDATE }>, Data<IndexScheduler>>,
|
||||
index_uid: web::Path<String>,
|
||||
body: ValidatedJson<Settings<Unchecked>, DeserrError>,
|
||||
body: ValidatedJson<Settings<Unchecked>, DeserrJsonError>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
|
||||
let new_settings = body.into_inner();
|
||||
|
||||
analytics.publish(
|
||||
|
@ -570,6 +579,8 @@ pub async fn get_all(
|
|||
index_scheduler: GuardedData<ActionPolicy<{ actions::SETTINGS_GET }>, Data<IndexScheduler>>,
|
||||
index_uid: web::Path<String>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
|
||||
let index = index_scheduler.index(&index_uid)?;
|
||||
let rtxn = index.read_txn()?;
|
||||
let new_settings = settings(&index, &rtxn)?;
|
||||
|
@ -581,6 +592,8 @@ pub async fn delete_all(
|
|||
index_scheduler: GuardedData<ActionPolicy<{ actions::SETTINGS_UPDATE }>, Data<IndexScheduler>>,
|
||||
index_uid: web::Path<String>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let index_uid = IndexUid::try_from(index_uid.into_inner())?;
|
||||
|
||||
let new_settings = Settings::cleared().into_unchecked();
|
||||
|
||||
let allow_index_creation = index_scheduler.filters().allow_index_creation;
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::str::FromStr;
|
||||
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use index_scheduler::{IndexScheduler, Query};
|
||||
use log::debug;
|
||||
use meilisearch_types::error::{ResponseError, TakeErrorMessage};
|
||||
use meilisearch_auth::AuthController;
|
||||
use meilisearch_types::error::ResponseError;
|
||||
use meilisearch_types::settings::{Settings, Unchecked};
|
||||
use meilisearch_types::star_or::StarOr;
|
||||
use meilisearch_types::tasks::{Kind, Status, Task, TaskId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
@ -35,37 +34,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
|||
.service(web::scope("/swap-indexes").configure(swap_indexes::configure));
|
||||
}
|
||||
|
||||
/// Extracts the raw values from the `StarOr` types and
|
||||
/// return None if a `StarOr::Star` is encountered.
|
||||
pub fn fold_star_or<T, O>(content: impl IntoIterator<Item = StarOr<T>>) -> Option<O>
|
||||
where
|
||||
O: FromIterator<T>,
|
||||
{
|
||||
content
|
||||
.into_iter()
|
||||
.map(|value| match value {
|
||||
StarOr::Star => None,
|
||||
StarOr::Other(val) => Some(val),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn from_string_to_option<T, E>(input: &str) -> Result<Option<T>, E>
|
||||
where
|
||||
T: FromStr<Err = E>,
|
||||
{
|
||||
Ok(Some(input.parse()?))
|
||||
}
|
||||
pub fn from_string_to_option_take_error_message<T, E>(
|
||||
input: &str,
|
||||
) -> Result<Option<T>, TakeErrorMessage<E>>
|
||||
where
|
||||
T: FromStr<Err = E>,
|
||||
{
|
||||
Ok(Some(input.parse().map_err(TakeErrorMessage)?))
|
||||
}
|
||||
|
||||
const PAGINATION_DEFAULT_LIMIT: fn() -> usize = || 20;
|
||||
const PAGINATION_DEFAULT_LIMIT: usize = 20;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
@ -262,13 +231,15 @@ pub struct Stats {
|
|||
|
||||
async fn get_stats(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::STATS_GET }>, Data<IndexScheduler>>,
|
||||
auth_controller: GuardedData<ActionPolicy<{ actions::STATS_GET }>, AuthController>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
analytics.publish("Stats Seen".to_string(), json!({ "per_index_uid": false }), Some(&req));
|
||||
let search_rules = &index_scheduler.filters().search_rules;
|
||||
|
||||
let stats = create_all_stats((*index_scheduler).clone(), search_rules)?;
|
||||
let stats =
|
||||
create_all_stats((*index_scheduler).clone(), (*auth_controller).clone(), search_rules)?;
|
||||
|
||||
debug!("returns: {:?}", stats);
|
||||
Ok(HttpResponse::Ok().json(stats))
|
||||
|
@ -276,6 +247,7 @@ async fn get_stats(
|
|||
|
||||
pub fn create_all_stats(
|
||||
index_scheduler: Data<IndexScheduler>,
|
||||
auth_controller: AuthController,
|
||||
search_rules: &meilisearch_auth::SearchRules,
|
||||
) -> Result<Stats, ResponseError> {
|
||||
let mut last_task: Option<OffsetDateTime> = None;
|
||||
|
@ -285,6 +257,7 @@ pub fn create_all_stats(
|
|||
Query { statuses: Some(vec![Status::Processing]), limit: Some(1), ..Query::default() },
|
||||
search_rules.authorized_indexes(),
|
||||
)?;
|
||||
// accumulate the size of each indexes
|
||||
let processing_index = processing_task.first().and_then(|task| task.index_uid());
|
||||
for (name, index) in index_scheduler.indexes()? {
|
||||
if !search_rules.is_index_authorized(&name) {
|
||||
|
@ -305,6 +278,11 @@ pub fn create_all_stats(
|
|||
|
||||
indexes.insert(name, stats);
|
||||
}
|
||||
|
||||
database_size += index_scheduler.size()?;
|
||||
database_size += auth_controller.size()?;
|
||||
database_size += index_scheduler.compute_update_file_size()?;
|
||||
|
||||
let stats = Stats { database_size, last_update: last_task, indexes };
|
||||
Ok(stats)
|
||||
}
|
||||
|
|
|
@ -2,8 +2,10 @@ use actix_web::web::Data;
|
|||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use deserr::DeserializeFromValue;
|
||||
use index_scheduler::IndexScheduler;
|
||||
use meilisearch_types::deserr::DeserrJsonError;
|
||||
use meilisearch_types::error::deserr_codes::InvalidSwapIndexes;
|
||||
use meilisearch_types::error::{DeserrError, ResponseError};
|
||||
use meilisearch_types::error::ResponseError;
|
||||
use meilisearch_types::index_uid::IndexUid;
|
||||
use meilisearch_types::tasks::{IndexSwap, KindWithContent};
|
||||
use serde_json::json;
|
||||
|
||||
|
@ -20,15 +22,15 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
|||
}
|
||||
|
||||
#[derive(DeserializeFromValue, Debug, Clone, PartialEq, Eq)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
#[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)]
|
||||
pub struct SwapIndexesPayload {
|
||||
#[deserr(error = DeserrError<InvalidSwapIndexes>)]
|
||||
indexes: Vec<String>,
|
||||
#[deserr(error = DeserrJsonError<InvalidSwapIndexes>, missing_field_error = DeserrJsonError::missing_swap_indexes)]
|
||||
indexes: Vec<IndexUid>,
|
||||
}
|
||||
|
||||
pub async fn swap_indexes(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::INDEXES_SWAP }>, Data<IndexScheduler>>,
|
||||
params: ValidatedJson<Vec<SwapIndexesPayload>, DeserrError>,
|
||||
params: ValidatedJson<Vec<SwapIndexesPayload>, DeserrJsonError>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
|
@ -44,6 +46,7 @@ pub async fn swap_indexes(
|
|||
|
||||
let mut swaps = vec![];
|
||||
for SwapIndexesPayload { indexes } in params.into_iter() {
|
||||
// TODO: switch to deserr
|
||||
let (lhs, rhs) = match indexes.as_slice() {
|
||||
[lhs, rhs] => (lhs, rhs),
|
||||
_ => {
|
||||
|
@ -53,7 +56,7 @@ pub async fn swap_indexes(
|
|||
if !search_rules.is_index_authorized(lhs) || !search_rules.is_index_authorized(rhs) {
|
||||
return Err(AuthenticationError::InvalidToken.into());
|
||||
}
|
||||
swaps.push(IndexSwap { indexes: (lhs.clone(), rhs.clone()) });
|
||||
swaps.push(IndexSwap { indexes: (lhs.to_string(), rhs.to_string()) });
|
||||
}
|
||||
|
||||
let task = KindWithContent::IndexSwap { swaps };
|
||||
|
|
|
@ -1,34 +1,32 @@
|
|||
use std::num::ParseIntError;
|
||||
use std::str::FromStr;
|
||||
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use deserr::DeserializeFromValue;
|
||||
use index_scheduler::{IndexScheduler, Query, TaskId};
|
||||
use meilisearch_types::deserr::query_params::Param;
|
||||
use meilisearch_types::deserr::DeserrQueryParamError;
|
||||
use meilisearch_types::error::deserr_codes::*;
|
||||
use meilisearch_types::error::{DeserrError, ResponseError, TakeErrorMessage};
|
||||
use meilisearch_types::error::{InvalidTaskDateError, ResponseError};
|
||||
use meilisearch_types::index_uid::IndexUid;
|
||||
use meilisearch_types::settings::{Settings, Unchecked};
|
||||
use meilisearch_types::star_or::StarOr;
|
||||
use meilisearch_types::star_or::{OptionStarOr, OptionStarOrList};
|
||||
use meilisearch_types::tasks::{
|
||||
serialize_duration, Details, IndexSwap, Kind, KindWithContent, Status, Task,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_cs::vec::CS;
|
||||
use serde::Serialize;
|
||||
use serde_json::json;
|
||||
use time::format_description::well_known::Rfc3339;
|
||||
use time::macros::format_description;
|
||||
use time::{Date, Duration, OffsetDateTime, Time};
|
||||
use tokio::task;
|
||||
|
||||
use super::{fold_star_or, SummarizedTaskView};
|
||||
use super::SummarizedTaskView;
|
||||
use crate::analytics::Analytics;
|
||||
use crate::extractors::authentication::policies::*;
|
||||
use crate::extractors::authentication::GuardedData;
|
||||
use crate::extractors::query_parameters::QueryParameter;
|
||||
use crate::extractors::sequential_extractor::SeqHandler;
|
||||
|
||||
const DEFAULT_LIMIT: fn() -> u32 = || 20;
|
||||
const DEFAULT_LIMIT: u32 = 20;
|
||||
|
||||
pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
|
@ -164,162 +162,157 @@ impl From<Details> for DetailsView {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_option_cs<T: FromStr>(
|
||||
s: Option<CS<String>>,
|
||||
) -> Result<Option<Vec<T>>, TakeErrorMessage<T::Err>> {
|
||||
if let Some(s) = s {
|
||||
s.into_iter()
|
||||
.map(|s| T::from_str(&s))
|
||||
.collect::<Result<Vec<T>, T::Err>>()
|
||||
.map_err(TakeErrorMessage)
|
||||
.map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
#[derive(Debug, DeserializeFromValue)]
|
||||
#[deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields)]
|
||||
pub struct TasksFilterQuery {
|
||||
#[deserr(default = Param(DEFAULT_LIMIT), error = DeserrQueryParamError<InvalidTaskLimit>)]
|
||||
pub limit: Param<u32>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskFrom>)]
|
||||
pub from: Option<Param<TaskId>>,
|
||||
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskUids>)]
|
||||
pub uids: OptionStarOrList<u32>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskCanceledBy>)]
|
||||
pub canceled_by: OptionStarOrList<u32>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskTypes>)]
|
||||
pub types: OptionStarOrList<Kind>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskStatuses>)]
|
||||
pub statuses: OptionStarOrList<Status>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidIndexUid>)]
|
||||
pub index_uids: OptionStarOrList<IndexUid>,
|
||||
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskAfterEnqueuedAt>, from(OptionStarOr<String>) = deserialize_date_after -> InvalidTaskDateError)]
|
||||
pub after_enqueued_at: OptionStarOr<OffsetDateTime>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskBeforeEnqueuedAt>, from(OptionStarOr<String>) = deserialize_date_before -> InvalidTaskDateError)]
|
||||
pub before_enqueued_at: OptionStarOr<OffsetDateTime>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskAfterStartedAt>, from(OptionStarOr<String>) = deserialize_date_after -> InvalidTaskDateError)]
|
||||
pub after_started_at: OptionStarOr<OffsetDateTime>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskBeforeStartedAt>, from(OptionStarOr<String>) = deserialize_date_before -> InvalidTaskDateError)]
|
||||
pub before_started_at: OptionStarOr<OffsetDateTime>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskAfterFinishedAt>, from(OptionStarOr<String>) = deserialize_date_after -> InvalidTaskDateError)]
|
||||
pub after_finished_at: OptionStarOr<OffsetDateTime>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskBeforeFinishedAt>, from(OptionStarOr<String>) = deserialize_date_before -> InvalidTaskDateError)]
|
||||
pub before_finished_at: OptionStarOr<OffsetDateTime>,
|
||||
}
|
||||
fn parse_option_cs_star_or<T: FromStr>(
|
||||
s: Option<CS<StarOr<String>>>,
|
||||
) -> Result<Option<Vec<T>>, TakeErrorMessage<T::Err>> {
|
||||
if let Some(s) = s.and_then(fold_star_or) as Option<Vec<String>> {
|
||||
s.into_iter()
|
||||
.map(|s| T::from_str(&s))
|
||||
.collect::<Result<Vec<T>, T::Err>>()
|
||||
.map_err(TakeErrorMessage)
|
||||
.map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
fn parse_option_str<T: FromStr>(s: Option<String>) -> Result<Option<T>, TakeErrorMessage<T::Err>> {
|
||||
if let Some(s) = s {
|
||||
T::from_str(&s).map_err(TakeErrorMessage).map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
impl TasksFilterQuery {
|
||||
fn into_query(self) -> Query {
|
||||
Query {
|
||||
limit: Some(self.limit.0),
|
||||
from: self.from.as_deref().copied(),
|
||||
statuses: self.statuses.merge_star_and_none(),
|
||||
types: self.types.merge_star_and_none(),
|
||||
index_uids: self.index_uids.map(|x| x.to_string()).merge_star_and_none(),
|
||||
uids: self.uids.merge_star_and_none(),
|
||||
canceled_by: self.canceled_by.merge_star_and_none(),
|
||||
before_enqueued_at: self.before_enqueued_at.merge_star_and_none(),
|
||||
after_enqueued_at: self.after_enqueued_at.merge_star_and_none(),
|
||||
before_started_at: self.before_started_at.merge_star_and_none(),
|
||||
after_started_at: self.after_started_at.merge_star_and_none(),
|
||||
before_finished_at: self.before_finished_at.merge_star_and_none(),
|
||||
after_finished_at: self.after_finished_at.merge_star_and_none(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_str<T: FromStr>(s: String) -> Result<T, TakeErrorMessage<T::Err>> {
|
||||
T::from_str(&s).map_err(TakeErrorMessage)
|
||||
impl TaskDeletionOrCancelationQuery {
|
||||
fn is_empty(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
TaskDeletionOrCancelationQuery {
|
||||
uids: OptionStarOrList::None,
|
||||
canceled_by: OptionStarOrList::None,
|
||||
types: OptionStarOrList::None,
|
||||
statuses: OptionStarOrList::None,
|
||||
index_uids: OptionStarOrList::None,
|
||||
after_enqueued_at: OptionStarOr::None,
|
||||
before_enqueued_at: OptionStarOr::None,
|
||||
after_started_at: OptionStarOr::None,
|
||||
before_started_at: OptionStarOr::None,
|
||||
after_finished_at: OptionStarOr::None,
|
||||
before_finished_at: OptionStarOr::None
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, DeserializeFromValue)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
pub struct TasksFilterQuery {
|
||||
#[deserr(error = DeserrError<InvalidTaskLimit>, default = DEFAULT_LIMIT(), from(String) = parse_str::<u32> -> TakeErrorMessage<ParseIntError>)]
|
||||
pub limit: u32,
|
||||
#[deserr(error = DeserrError<InvalidTaskFrom>, from(Option<String>) = parse_option_str::<TaskId> -> TakeErrorMessage<ParseIntError>)]
|
||||
pub from: Option<TaskId>,
|
||||
|
||||
#[deserr(error = DeserrError<InvalidTaskUids>, from(Option<CS<String>>) = parse_option_cs::<u32> -> TakeErrorMessage<ParseIntError>)]
|
||||
pub uids: Option<Vec<u32>>,
|
||||
#[deserr(error = DeserrError<InvalidTaskCanceledBy>, from(Option<CS<String>>) = parse_option_cs::<u32> -> TakeErrorMessage<ParseIntError>)]
|
||||
pub canceled_by: Option<Vec<u32>>,
|
||||
#[deserr(error = DeserrError<InvalidTaskTypes>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Kind> -> TakeErrorMessage<ResponseError>)]
|
||||
pub types: Option<Vec<Kind>>,
|
||||
#[deserr(error = DeserrError<InvalidTaskStatuses>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Status> -> TakeErrorMessage<ResponseError>)]
|
||||
pub statuses: Option<Vec<Status>>,
|
||||
#[deserr(error = DeserrError<InvalidIndexUid>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<IndexUid> -> TakeErrorMessage<ResponseError>)]
|
||||
pub index_uids: Option<Vec<IndexUid>>,
|
||||
|
||||
#[deserr(error = DeserrError<InvalidTaskAfterEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
|
||||
pub after_enqueued_at: Option<OffsetDateTime>,
|
||||
#[deserr(error = DeserrError<InvalidTaskBeforeEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
|
||||
pub before_enqueued_at: Option<OffsetDateTime>,
|
||||
#[deserr(error = DeserrError<InvalidTaskAfterStartedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
|
||||
pub after_started_at: Option<OffsetDateTime>,
|
||||
#[deserr(error = DeserrError<InvalidTaskBeforeStartedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
|
||||
pub before_started_at: Option<OffsetDateTime>,
|
||||
#[deserr(error = DeserrError<InvalidTaskAfterFinishedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
|
||||
pub after_finished_at: Option<OffsetDateTime>,
|
||||
#[deserr(error = DeserrError<InvalidTaskBeforeFinishedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
|
||||
pub before_finished_at: Option<OffsetDateTime>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, DeserializeFromValue)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
#[deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields)]
|
||||
pub struct TaskDeletionOrCancelationQuery {
|
||||
#[deserr(error = DeserrError<InvalidTaskUids>, from(Option<CS<String>>) = parse_option_cs::<u32> -> TakeErrorMessage<ParseIntError>)]
|
||||
pub uids: Option<Vec<u32>>,
|
||||
#[deserr(error = DeserrError<InvalidTaskCanceledBy>, from(Option<CS<String>>) = parse_option_cs::<u32> -> TakeErrorMessage<ParseIntError>)]
|
||||
pub canceled_by: Option<Vec<u32>>,
|
||||
#[deserr(error = DeserrError<InvalidTaskTypes>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Kind> -> TakeErrorMessage<ResponseError>)]
|
||||
pub types: Option<Vec<Kind>>,
|
||||
#[deserr(error = DeserrError<InvalidTaskStatuses>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Status> -> TakeErrorMessage<ResponseError>)]
|
||||
pub statuses: Option<Vec<Status>>,
|
||||
#[deserr(error = DeserrError<InvalidIndexUid>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<IndexUid> -> TakeErrorMessage<ResponseError>)]
|
||||
pub index_uids: Option<Vec<IndexUid>>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskUids>)]
|
||||
pub uids: OptionStarOrList<u32>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskCanceledBy>)]
|
||||
pub canceled_by: OptionStarOrList<u32>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskTypes>)]
|
||||
pub types: OptionStarOrList<Kind>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskStatuses>)]
|
||||
pub statuses: OptionStarOrList<Status>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidIndexUid>)]
|
||||
pub index_uids: OptionStarOrList<IndexUid>,
|
||||
|
||||
#[deserr(error = DeserrError<InvalidTaskAfterEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
|
||||
pub after_enqueued_at: Option<OffsetDateTime>,
|
||||
#[deserr(error = DeserrError<InvalidTaskBeforeEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
|
||||
pub before_enqueued_at: Option<OffsetDateTime>,
|
||||
#[deserr(error = DeserrError<InvalidTaskAfterStartedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
|
||||
pub after_started_at: Option<OffsetDateTime>,
|
||||
#[deserr(error = DeserrError<InvalidTaskBeforeStartedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
|
||||
pub before_started_at: Option<OffsetDateTime>,
|
||||
#[deserr(error = DeserrError<InvalidTaskAfterFinishedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>)]
|
||||
pub after_finished_at: Option<OffsetDateTime>,
|
||||
#[deserr(error = DeserrError<InvalidTaskBeforeFinishedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>)]
|
||||
pub before_finished_at: Option<OffsetDateTime>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskAfterEnqueuedAt>, from(OptionStarOr<String>) = deserialize_date_after -> InvalidTaskDateError)]
|
||||
pub after_enqueued_at: OptionStarOr<OffsetDateTime>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskBeforeEnqueuedAt>, from(OptionStarOr<String>) = deserialize_date_before -> InvalidTaskDateError)]
|
||||
pub before_enqueued_at: OptionStarOr<OffsetDateTime>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskAfterStartedAt>, from(OptionStarOr<String>) = deserialize_date_after -> InvalidTaskDateError)]
|
||||
pub after_started_at: OptionStarOr<OffsetDateTime>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskBeforeStartedAt>, from(OptionStarOr<String>) = deserialize_date_before -> InvalidTaskDateError)]
|
||||
pub before_started_at: OptionStarOr<OffsetDateTime>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskAfterFinishedAt>, from(OptionStarOr<String>) = deserialize_date_after -> InvalidTaskDateError)]
|
||||
pub after_finished_at: OptionStarOr<OffsetDateTime>,
|
||||
#[deserr(default, error = DeserrQueryParamError<InvalidTaskBeforeFinishedAt>, from(OptionStarOr<String>) = deserialize_date_before -> InvalidTaskDateError)]
|
||||
pub before_finished_at: OptionStarOr<OffsetDateTime>,
|
||||
}
|
||||
impl TaskDeletionOrCancelationQuery {
|
||||
fn into_query(self) -> Query {
|
||||
Query {
|
||||
limit: None,
|
||||
from: None,
|
||||
statuses: self.statuses.merge_star_and_none(),
|
||||
types: self.types.merge_star_and_none(),
|
||||
index_uids: self.index_uids.map(|x| x.to_string()).merge_star_and_none(),
|
||||
uids: self.uids.merge_star_and_none(),
|
||||
canceled_by: self.canceled_by.merge_star_and_none(),
|
||||
before_enqueued_at: self.before_enqueued_at.merge_star_and_none(),
|
||||
after_enqueued_at: self.after_enqueued_at.merge_star_and_none(),
|
||||
before_started_at: self.before_started_at.merge_star_and_none(),
|
||||
after_started_at: self.after_started_at.merge_star_and_none(),
|
||||
before_finished_at: self.before_finished_at.merge_star_and_none(),
|
||||
after_finished_at: self.after_finished_at.merge_star_and_none(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn cancel_tasks(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::TASKS_CANCEL }>, Data<IndexScheduler>>,
|
||||
params: QueryParameter<TaskDeletionOrCancelationQuery, DeserrError>,
|
||||
params: QueryParameter<TaskDeletionOrCancelationQuery, DeserrQueryParamError>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let TaskDeletionOrCancelationQuery {
|
||||
types,
|
||||
uids,
|
||||
canceled_by,
|
||||
statuses,
|
||||
index_uids,
|
||||
after_enqueued_at,
|
||||
before_enqueued_at,
|
||||
after_started_at,
|
||||
before_started_at,
|
||||
after_finished_at,
|
||||
before_finished_at,
|
||||
} = params.into_inner();
|
||||
let params = params.into_inner();
|
||||
|
||||
if params.is_empty() {
|
||||
return Err(index_scheduler::Error::TaskCancelationWithEmptyQuery.into());
|
||||
}
|
||||
|
||||
analytics.publish(
|
||||
"Tasks Canceled".to_string(),
|
||||
json!({
|
||||
"filtered_by_uid": uids.is_some(),
|
||||
"filtered_by_index_uid": index_uids.is_some(),
|
||||
"filtered_by_type": types.is_some(),
|
||||
"filtered_by_status": statuses.is_some(),
|
||||
"filtered_by_canceled_by": canceled_by.is_some(),
|
||||
"filtered_by_before_enqueued_at": before_enqueued_at.is_some(),
|
||||
"filtered_by_after_enqueued_at": after_enqueued_at.is_some(),
|
||||
"filtered_by_before_started_at": before_started_at.is_some(),
|
||||
"filtered_by_after_started_at": after_started_at.is_some(),
|
||||
"filtered_by_before_finished_at": before_finished_at.is_some(),
|
||||
"filtered_by_after_finished_at": after_finished_at.is_some(),
|
||||
"filtered_by_uid": params.uids.is_some(),
|
||||
"filtered_by_index_uid": params.index_uids.is_some(),
|
||||
"filtered_by_type": params.types.is_some(),
|
||||
"filtered_by_status": params.statuses.is_some(),
|
||||
"filtered_by_canceled_by": params.canceled_by.is_some(),
|
||||
"filtered_by_before_enqueued_at": params.before_enqueued_at.is_some(),
|
||||
"filtered_by_after_enqueued_at": params.after_enqueued_at.is_some(),
|
||||
"filtered_by_before_started_at": params.before_started_at.is_some(),
|
||||
"filtered_by_after_started_at": params.after_started_at.is_some(),
|
||||
"filtered_by_before_finished_at": params.before_finished_at.is_some(),
|
||||
"filtered_by_after_finished_at": params.after_finished_at.is_some(),
|
||||
}),
|
||||
Some(&req),
|
||||
);
|
||||
|
||||
let query = Query {
|
||||
limit: None,
|
||||
from: None,
|
||||
statuses,
|
||||
types,
|
||||
index_uids: index_uids.map(|xs| xs.into_iter().map(|s| s.to_string()).collect()),
|
||||
uids,
|
||||
canceled_by,
|
||||
before_enqueued_at,
|
||||
after_enqueued_at,
|
||||
before_started_at,
|
||||
after_started_at,
|
||||
before_finished_at,
|
||||
after_finished_at,
|
||||
};
|
||||
|
||||
if query.is_empty() {
|
||||
return Err(index_scheduler::Error::TaskCancelationWithEmptyQuery.into());
|
||||
}
|
||||
let query = params.into_query();
|
||||
|
||||
let tasks = index_scheduler.get_task_ids_from_authorized_indexes(
|
||||
&index_scheduler.read_txn()?,
|
||||
|
@ -337,62 +330,34 @@ async fn cancel_tasks(
|
|||
|
||||
async fn delete_tasks(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::TASKS_DELETE }>, Data<IndexScheduler>>,
|
||||
params: QueryParameter<TaskDeletionOrCancelationQuery, DeserrError>,
|
||||
params: QueryParameter<TaskDeletionOrCancelationQuery, DeserrQueryParamError>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let TaskDeletionOrCancelationQuery {
|
||||
types,
|
||||
uids,
|
||||
canceled_by,
|
||||
statuses,
|
||||
index_uids,
|
||||
let params = params.into_inner();
|
||||
|
||||
after_enqueued_at,
|
||||
before_enqueued_at,
|
||||
after_started_at,
|
||||
before_started_at,
|
||||
after_finished_at,
|
||||
before_finished_at,
|
||||
} = params.into_inner();
|
||||
if params.is_empty() {
|
||||
return Err(index_scheduler::Error::TaskDeletionWithEmptyQuery.into());
|
||||
}
|
||||
|
||||
analytics.publish(
|
||||
"Tasks Deleted".to_string(),
|
||||
json!({
|
||||
"filtered_by_uid": uids.is_some(),
|
||||
"filtered_by_index_uid": index_uids.is_some(),
|
||||
"filtered_by_type": types.is_some(),
|
||||
"filtered_by_status": statuses.is_some(),
|
||||
"filtered_by_canceled_by": canceled_by.is_some(),
|
||||
"filtered_by_before_enqueued_at": before_enqueued_at.is_some(),
|
||||
"filtered_by_after_enqueued_at": after_enqueued_at.is_some(),
|
||||
"filtered_by_before_started_at": before_started_at.is_some(),
|
||||
"filtered_by_after_started_at": after_started_at.is_some(),
|
||||
"filtered_by_before_finished_at": before_finished_at.is_some(),
|
||||
"filtered_by_after_finished_at": after_finished_at.is_some(),
|
||||
"filtered_by_uid": params.uids.is_some(),
|
||||
"filtered_by_index_uid": params.index_uids.is_some(),
|
||||
"filtered_by_type": params.types.is_some(),
|
||||
"filtered_by_status": params.statuses.is_some(),
|
||||
"filtered_by_canceled_by": params.canceled_by.is_some(),
|
||||
"filtered_by_before_enqueued_at": params.before_enqueued_at.is_some(),
|
||||
"filtered_by_after_enqueued_at": params.after_enqueued_at.is_some(),
|
||||
"filtered_by_before_started_at": params.before_started_at.is_some(),
|
||||
"filtered_by_after_started_at": params.after_started_at.is_some(),
|
||||
"filtered_by_before_finished_at": params.before_finished_at.is_some(),
|
||||
"filtered_by_after_finished_at": params.after_finished_at.is_some(),
|
||||
}),
|
||||
Some(&req),
|
||||
);
|
||||
|
||||
let query = Query {
|
||||
limit: None,
|
||||
from: None,
|
||||
statuses,
|
||||
types,
|
||||
index_uids: index_uids.map(|xs| xs.into_iter().map(|s| s.to_string()).collect()),
|
||||
uids,
|
||||
canceled_by,
|
||||
after_enqueued_at,
|
||||
before_enqueued_at,
|
||||
after_started_at,
|
||||
before_started_at,
|
||||
after_finished_at,
|
||||
before_finished_at,
|
||||
};
|
||||
|
||||
if query.is_empty() {
|
||||
return Err(index_scheduler::Error::TaskDeletionWithEmptyQuery.into());
|
||||
}
|
||||
let query = params.into_query();
|
||||
|
||||
let tasks = index_scheduler.get_task_ids_from_authorized_indexes(
|
||||
&index_scheduler.read_txn()?,
|
||||
|
@ -418,47 +383,17 @@ pub struct AllTasks {
|
|||
|
||||
async fn get_tasks(
|
||||
index_scheduler: GuardedData<ActionPolicy<{ actions::TASKS_GET }>, Data<IndexScheduler>>,
|
||||
params: QueryParameter<TasksFilterQuery, DeserrError>,
|
||||
params: QueryParameter<TasksFilterQuery, DeserrQueryParamError>,
|
||||
req: HttpRequest,
|
||||
analytics: web::Data<dyn Analytics>,
|
||||
) -> Result<HttpResponse, ResponseError> {
|
||||
let params = params.into_inner();
|
||||
let mut params = params.into_inner();
|
||||
analytics.get_tasks(¶ms, &req);
|
||||
|
||||
let TasksFilterQuery {
|
||||
types,
|
||||
uids,
|
||||
canceled_by,
|
||||
statuses,
|
||||
index_uids,
|
||||
limit,
|
||||
from,
|
||||
after_enqueued_at,
|
||||
before_enqueued_at,
|
||||
after_started_at,
|
||||
before_started_at,
|
||||
after_finished_at,
|
||||
before_finished_at,
|
||||
} = params;
|
||||
|
||||
// We +1 just to know if there is more after this "page" or not.
|
||||
let limit = limit.saturating_add(1);
|
||||
|
||||
let query = index_scheduler::Query {
|
||||
limit: Some(limit),
|
||||
from,
|
||||
statuses,
|
||||
types,
|
||||
index_uids: index_uids.map(|xs| xs.into_iter().map(|s| s.to_string()).collect()),
|
||||
uids,
|
||||
canceled_by,
|
||||
before_enqueued_at,
|
||||
after_enqueued_at,
|
||||
before_started_at,
|
||||
after_started_at,
|
||||
before_finished_at,
|
||||
after_finished_at,
|
||||
};
|
||||
params.limit.0 = params.limit.0.saturating_add(1);
|
||||
let limit = params.limit.0;
|
||||
let query = params.into_query();
|
||||
|
||||
let mut tasks_results: Vec<TaskView> = index_scheduler
|
||||
.get_tasks_from_authorized_indexes(
|
||||
|
@ -524,7 +459,7 @@ pub enum DeserializeDateOption {
|
|||
pub fn deserialize_date(
|
||||
value: &str,
|
||||
option: DeserializeDateOption,
|
||||
) -> std::result::Result<OffsetDateTime, TakeErrorMessage<InvalidTaskDateError>> {
|
||||
) -> std::result::Result<OffsetDateTime, InvalidTaskDateError> {
|
||||
// We can't parse using time's rfc3339 format, since then we won't know what part of the
|
||||
// datetime was not explicitly specified, and thus we won't be able to increment it to the
|
||||
// next step.
|
||||
|
@ -546,54 +481,41 @@ pub fn deserialize_date(
|
|||
}
|
||||
}
|
||||
} else {
|
||||
Err(TakeErrorMessage(InvalidTaskDateError(value.to_owned())))
|
||||
Err(InvalidTaskDateError(value.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deserialize_date_before(
|
||||
value: Option<String>,
|
||||
) -> std::result::Result<Option<OffsetDateTime>, TakeErrorMessage<InvalidTaskDateError>> {
|
||||
if let Some(value) = value {
|
||||
let date = deserialize_date(&value, DeserializeDateOption::Before)?;
|
||||
Ok(Some(date))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
pub fn deserialize_date_after(
|
||||
value: Option<String>,
|
||||
) -> std::result::Result<Option<OffsetDateTime>, TakeErrorMessage<InvalidTaskDateError>> {
|
||||
if let Some(value) = value {
|
||||
let date = deserialize_date(&value, DeserializeDateOption::After)?;
|
||||
Ok(Some(date))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
value: OptionStarOr<String>,
|
||||
) -> std::result::Result<OptionStarOr<OffsetDateTime>, InvalidTaskDateError> {
|
||||
value.try_map(|x| deserialize_date(&x, DeserializeDateOption::After))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidTaskDateError(String);
|
||||
impl std::fmt::Display for InvalidTaskDateError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "`{}` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.", self.0)
|
||||
}
|
||||
pub fn deserialize_date_before(
|
||||
value: OptionStarOr<String>,
|
||||
) -> std::result::Result<OptionStarOr<OffsetDateTime>, InvalidTaskDateError> {
|
||||
value.try_map(|x| deserialize_date(&x, DeserializeDateOption::Before))
|
||||
}
|
||||
impl std::error::Error for InvalidTaskDateError {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use deserr::DeserializeFromValue;
|
||||
use meili_snap::snapshot;
|
||||
use meilisearch_types::error::DeserrError;
|
||||
use meilisearch_types::deserr::DeserrQueryParamError;
|
||||
use meilisearch_types::error::{Code, ResponseError};
|
||||
|
||||
use crate::extractors::query_parameters::QueryParameter;
|
||||
use crate::routes::tasks::{TaskDeletionOrCancelationQuery, TasksFilterQuery};
|
||||
|
||||
fn deserr_query_params<T>(j: &str) -> Result<T, actix_web::Error>
|
||||
fn deserr_query_params<T>(j: &str) -> Result<T, ResponseError>
|
||||
where
|
||||
T: DeserializeFromValue<DeserrError>,
|
||||
T: DeserializeFromValue<DeserrQueryParamError>,
|
||||
{
|
||||
QueryParameter::<T, DeserrError>::from_query(j).map(|p| p.0)
|
||||
let value = serde_urlencoded::from_str::<serde_json::Value>(j)
|
||||
.map_err(|e| ResponseError::from_msg(e.to_string(), Code::BadRequest))?;
|
||||
|
||||
match deserr::deserialize::<_, _, DeserrQueryParamError>(value) {
|
||||
Ok(data) => Ok(data),
|
||||
Err(e) => Err(ResponseError::from(e)),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -602,65 +524,113 @@ mod tests {
|
|||
let params = "afterEnqueuedAt=2021-12-03&beforeEnqueuedAt=2021-12-03&afterStartedAt=2021-12-03&beforeStartedAt=2021-12-03&afterFinishedAt=2021-12-03&beforeFinishedAt=2021-12-03";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
|
||||
snapshot!(format!("{:?}", query.after_enqueued_at.unwrap()), @"2021-12-04 0:00:00.0 +00:00:00");
|
||||
snapshot!(format!("{:?}", query.before_enqueued_at.unwrap()), @"2021-12-03 0:00:00.0 +00:00:00");
|
||||
snapshot!(format!("{:?}", query.after_started_at.unwrap()), @"2021-12-04 0:00:00.0 +00:00:00");
|
||||
snapshot!(format!("{:?}", query.before_started_at.unwrap()), @"2021-12-03 0:00:00.0 +00:00:00");
|
||||
snapshot!(format!("{:?}", query.after_finished_at.unwrap()), @"2021-12-04 0:00:00.0 +00:00:00");
|
||||
snapshot!(format!("{:?}", query.before_finished_at.unwrap()), @"2021-12-03 0:00:00.0 +00:00:00");
|
||||
snapshot!(format!("{:?}", query.after_enqueued_at), @"Other(2021-12-04 0:00:00.0 +00:00:00)");
|
||||
snapshot!(format!("{:?}", query.before_enqueued_at), @"Other(2021-12-03 0:00:00.0 +00:00:00)");
|
||||
snapshot!(format!("{:?}", query.after_started_at), @"Other(2021-12-04 0:00:00.0 +00:00:00)");
|
||||
snapshot!(format!("{:?}", query.before_started_at), @"Other(2021-12-03 0:00:00.0 +00:00:00)");
|
||||
snapshot!(format!("{:?}", query.after_finished_at), @"Other(2021-12-04 0:00:00.0 +00:00:00)");
|
||||
snapshot!(format!("{:?}", query.before_finished_at), @"Other(2021-12-03 0:00:00.0 +00:00:00)");
|
||||
}
|
||||
{
|
||||
let params =
|
||||
"afterEnqueuedAt=2021-12-03T23:45:23Z&beforeEnqueuedAt=2021-12-03T23:45:23Z";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
snapshot!(format!("{:?}", query.after_enqueued_at.unwrap()), @"2021-12-03 23:45:23.0 +00:00:00");
|
||||
snapshot!(format!("{:?}", query.before_enqueued_at.unwrap()), @"2021-12-03 23:45:23.0 +00:00:00");
|
||||
snapshot!(format!("{:?}", query.after_enqueued_at), @"Other(2021-12-03 23:45:23.0 +00:00:00)");
|
||||
snapshot!(format!("{:?}", query.before_enqueued_at), @"Other(2021-12-03 23:45:23.0 +00:00:00)");
|
||||
}
|
||||
{
|
||||
let params = "afterEnqueuedAt=1997-11-12T09:55:06-06:20";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
snapshot!(format!("{:?}", query.after_enqueued_at.unwrap()), @"1997-11-12 9:55:06.0 -06:20:00");
|
||||
snapshot!(format!("{:?}", query.after_enqueued_at), @"Other(1997-11-12 9:55:06.0 -06:20:00)");
|
||||
}
|
||||
{
|
||||
let params = "afterEnqueuedAt=1997-11-12T09:55:06%2B00:00";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
snapshot!(format!("{:?}", query.after_enqueued_at.unwrap()), @"1997-11-12 9:55:06.0 +00:00:00");
|
||||
snapshot!(format!("{:?}", query.after_enqueued_at), @"Other(1997-11-12 9:55:06.0 +00:00:00)");
|
||||
}
|
||||
{
|
||||
let params = "afterEnqueuedAt=1997-11-12T09:55:06.200000300Z";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
snapshot!(format!("{:?}", query.after_enqueued_at.unwrap()), @"1997-11-12 9:55:06.2000003 +00:00:00");
|
||||
snapshot!(format!("{:?}", query.after_enqueued_at), @"Other(1997-11-12 9:55:06.2000003 +00:00:00)");
|
||||
}
|
||||
{
|
||||
// Stars are allowed in date fields as well
|
||||
let params = "afterEnqueuedAt=*&beforeStartedAt=*&afterFinishedAt=*&beforeFinishedAt=*&afterStartedAt=*&beforeEnqueuedAt=*";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
snapshot!(format!("{:?}", query), @"TaskDeletionOrCancelationQuery { uids: None, canceled_by: None, types: None, statuses: None, index_uids: None, after_enqueued_at: Star, before_enqueued_at: Star, after_started_at: Star, before_started_at: Star, after_finished_at: Star, before_finished_at: Star }");
|
||||
}
|
||||
{
|
||||
let params = "afterFinishedAt=2021";
|
||||
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
|
||||
snapshot!(format!("{err}"), @"`2021` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterFinishedAt`.");
|
||||
snapshot!(meili_snap::json_string!(err), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `afterFinishedAt`: `2021` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_after_finished_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_after_finished_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
{
|
||||
let params = "beforeFinishedAt=2021";
|
||||
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
|
||||
snapshot!(format!("{err}"), @"`2021` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeFinishedAt`.");
|
||||
snapshot!(meili_snap::json_string!(err), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `beforeFinishedAt`: `2021` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_before_finished_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_before_finished_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
{
|
||||
let params = "afterEnqueuedAt=2021-12";
|
||||
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
|
||||
snapshot!(format!("{err}"), @"`2021-12` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterEnqueuedAt`.");
|
||||
snapshot!(meili_snap::json_string!(err), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `afterEnqueuedAt`: `2021-12` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_after_enqueued_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_after_enqueued_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
{
|
||||
let params = "beforeEnqueuedAt=2021-12-03T23";
|
||||
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
|
||||
snapshot!(format!("{err}"), @"`2021-12-03T23` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeEnqueuedAt`.");
|
||||
snapshot!(meili_snap::json_string!(err), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `beforeEnqueuedAt`: `2021-12-03T23` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_before_enqueued_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_before_enqueued_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
{
|
||||
let params = "afterStartedAt=2021-12-03T23:45";
|
||||
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
|
||||
snapshot!(format!("{err}"), @"`2021-12-03T23:45` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterStartedAt`.");
|
||||
snapshot!(meili_snap::json_string!(err), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `afterStartedAt`: `2021-12-03T23:45` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_after_started_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_after_started_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
{
|
||||
let params = "beforeStartedAt=2021-12-03T23:45";
|
||||
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
|
||||
snapshot!(format!("{err}"), @"`2021-12-03T23:45` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeStartedAt`.");
|
||||
snapshot!(meili_snap::json_string!(err), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `beforeStartedAt`: `2021-12-03T23:45` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_before_started_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_before_started_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -669,22 +639,48 @@ mod tests {
|
|||
{
|
||||
let params = "uids=78,1,12,73";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
snapshot!(format!("{:?}", query.uids.unwrap()), @"[78, 1, 12, 73]");
|
||||
snapshot!(format!("{:?}", query.uids), @"List([78, 1, 12, 73])");
|
||||
}
|
||||
{
|
||||
let params = "uids=1";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
snapshot!(format!("{:?}", query.uids.unwrap()), @"[1]");
|
||||
snapshot!(format!("{:?}", query.uids), @"List([1])");
|
||||
}
|
||||
{
|
||||
let params = "uids=cat,*,dog";
|
||||
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
|
||||
snapshot!(meili_snap::json_string!(err), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `uids[0]`: could not parse `cat` as a positive integer",
|
||||
"code": "invalid_task_uids",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_uids"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
{
|
||||
let params = "uids=78,hello,world";
|
||||
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
|
||||
snapshot!(format!("{err}"), @"invalid digit found in string at `.uids`.");
|
||||
snapshot!(meili_snap::json_string!(err), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `uids[1]`: could not parse `hello` as a positive integer",
|
||||
"code": "invalid_task_uids",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_uids"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
{
|
||||
let params = "uids=cat";
|
||||
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
|
||||
snapshot!(format!("{err}"), @"invalid digit found in string at `.uids`.");
|
||||
snapshot!(meili_snap::json_string!(err), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `uids`: could not parse `cat` as a positive integer",
|
||||
"code": "invalid_task_uids",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_uids"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -693,17 +689,24 @@ mod tests {
|
|||
{
|
||||
let params = "statuses=succeeded,failed,enqueued,processing,canceled";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
snapshot!(format!("{:?}", query.statuses.unwrap()), @"[Succeeded, Failed, Enqueued, Processing, Canceled]");
|
||||
snapshot!(format!("{:?}", query.statuses), @"List([Succeeded, Failed, Enqueued, Processing, Canceled])");
|
||||
}
|
||||
{
|
||||
let params = "statuses=enqueued";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
snapshot!(format!("{:?}", query.statuses.unwrap()), @"[Enqueued]");
|
||||
snapshot!(format!("{:?}", query.statuses), @"List([Enqueued])");
|
||||
}
|
||||
{
|
||||
let params = "statuses=finished";
|
||||
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
|
||||
snapshot!(format!("{err}"), @"`finished` is not a status. Available status are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. at `.statuses`.");
|
||||
snapshot!(meili_snap::json_string!(err), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `statuses`: `finished` is not a valid task status. Available statuses are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`.",
|
||||
"code": "invalid_task_statuses",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_statuses"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
|
@ -711,17 +714,24 @@ mod tests {
|
|||
{
|
||||
let params = "types=documentAdditionOrUpdate,documentDeletion,settingsUpdate,indexCreation,indexDeletion,indexUpdate,indexSwap,taskCancelation,taskDeletion,dumpCreation,snapshotCreation";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
snapshot!(format!("{:?}", query.types.unwrap()), @"[DocumentAdditionOrUpdate, DocumentDeletion, SettingsUpdate, IndexCreation, IndexDeletion, IndexUpdate, IndexSwap, TaskCancelation, TaskDeletion, DumpCreation, SnapshotCreation]");
|
||||
snapshot!(format!("{:?}", query.types), @"List([DocumentAdditionOrUpdate, DocumentDeletion, SettingsUpdate, IndexCreation, IndexDeletion, IndexUpdate, IndexSwap, TaskCancelation, TaskDeletion, DumpCreation, SnapshotCreation])");
|
||||
}
|
||||
{
|
||||
let params = "types=settingsUpdate";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
snapshot!(format!("{:?}", query.types.unwrap()), @"[SettingsUpdate]");
|
||||
snapshot!(format!("{:?}", query.types), @"List([SettingsUpdate])");
|
||||
}
|
||||
{
|
||||
let params = "types=createIndex";
|
||||
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
|
||||
snapshot!(format!("{err}"), @"`createIndex` is not a type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`. at `.types`.");
|
||||
snapshot!(meili_snap::json_string!(err), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `types`: `createIndex` is not a valid task type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`.",
|
||||
"code": "invalid_task_types",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_types"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
|
@ -729,22 +739,36 @@ mod tests {
|
|||
{
|
||||
let params = "indexUids=toto,tata-78";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
snapshot!(format!("{:?}", query.index_uids.unwrap()), @r###"[IndexUid("toto"), IndexUid("tata-78")]"###);
|
||||
snapshot!(format!("{:?}", query.index_uids), @r###"List([IndexUid("toto"), IndexUid("tata-78")])"###);
|
||||
}
|
||||
{
|
||||
let params = "indexUids=index_a";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
snapshot!(format!("{:?}", query.index_uids.unwrap()), @r###"[IndexUid("index_a")]"###);
|
||||
snapshot!(format!("{:?}", query.index_uids), @r###"List([IndexUid("index_a")])"###);
|
||||
}
|
||||
{
|
||||
let params = "indexUids=1,hé";
|
||||
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
|
||||
snapshot!(format!("{err}"), @"`hé` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). at `.indexUids`.");
|
||||
snapshot!(meili_snap::json_string!(err), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `indexUids[1]`: `hé` 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"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
{
|
||||
let params = "indexUids=hé";
|
||||
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
|
||||
snapshot!(format!("{err}"), @"`hé` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). at `.indexUids`.");
|
||||
snapshot!(meili_snap::json_string!(err), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `indexUids`: `hé` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).",
|
||||
"code": "invalid_index_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -753,38 +777,74 @@ mod tests {
|
|||
{
|
||||
let params = "from=12&limit=15&indexUids=toto,tata-78&statuses=succeeded,enqueued&afterEnqueuedAt=2012-04-23&uids=1,2,3";
|
||||
let query = deserr_query_params::<TasksFilterQuery>(params).unwrap();
|
||||
snapshot!(format!("{:?}", query), @r###"TasksFilterQuery { limit: 15, from: Some(12), uids: Some([1, 2, 3]), canceled_by: None, types: None, statuses: Some([Succeeded, Enqueued]), index_uids: Some([IndexUid("toto"), IndexUid("tata-78")]), after_enqueued_at: Some(2012-04-24 0:00:00.0 +00:00:00), before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None }"###);
|
||||
snapshot!(format!("{:?}", query), @r###"TasksFilterQuery { limit: Param(15), from: Some(Param(12)), uids: List([1, 2, 3]), canceled_by: None, types: None, statuses: List([Succeeded, Enqueued]), index_uids: List([IndexUid("toto"), IndexUid("tata-78")]), after_enqueued_at: Other(2012-04-24 0:00:00.0 +00:00:00), before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None }"###);
|
||||
}
|
||||
{
|
||||
// Stars should translate to `None` in the query
|
||||
// Verify value of the default limit
|
||||
let params = "indexUids=*&statuses=succeeded,*&afterEnqueuedAt=2012-04-23&uids=1,2,3";
|
||||
let query = deserr_query_params::<TasksFilterQuery>(params).unwrap();
|
||||
snapshot!(format!("{:?}", query), @"TasksFilterQuery { limit: 20, from: None, uids: Some([1, 2, 3]), canceled_by: None, types: None, statuses: None, index_uids: None, after_enqueued_at: Some(2012-04-24 0:00:00.0 +00:00:00), before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None }");
|
||||
snapshot!(format!("{:?}", query), @"TasksFilterQuery { limit: Param(20), from: None, uids: List([1, 2, 3]), canceled_by: None, types: None, statuses: Star, index_uids: Star, after_enqueued_at: Other(2012-04-24 0:00:00.0 +00:00:00), before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None }");
|
||||
}
|
||||
{
|
||||
// Stars should also translate to `None` in task deletion/cancelation queries
|
||||
let params = "indexUids=*&statuses=succeeded,*&afterEnqueuedAt=2012-04-23&uids=1,2,3";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
snapshot!(format!("{:?}", query), @"TaskDeletionOrCancelationQuery { uids: Some([1, 2, 3]), canceled_by: None, types: None, statuses: None, index_uids: None, after_enqueued_at: Some(2012-04-24 0:00:00.0 +00:00:00), before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None }");
|
||||
snapshot!(format!("{:?}", query), @"TaskDeletionOrCancelationQuery { uids: List([1, 2, 3]), canceled_by: None, types: None, statuses: Star, index_uids: Star, after_enqueued_at: Other(2012-04-24 0:00:00.0 +00:00:00), before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None }");
|
||||
}
|
||||
{
|
||||
// Stars in uids not allowed
|
||||
let params = "uids=*";
|
||||
// Star in from not allowed
|
||||
let params = "uids=*&from=*";
|
||||
let err = deserr_query_params::<TasksFilterQuery>(params).unwrap_err();
|
||||
snapshot!(format!("{err}"), @"invalid digit found in string at `.uids`.");
|
||||
snapshot!(meili_snap::json_string!(err), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `from`: could not parse `*` as a positive integer",
|
||||
"code": "invalid_task_from",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_from"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
{
|
||||
// From not allowed in task deletion/cancelation queries
|
||||
let params = "from=12";
|
||||
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
|
||||
snapshot!(format!("{err}"), @"Json deserialize error: unknown field `from`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.");
|
||||
snapshot!(meili_snap::json_string!(err), @r###"
|
||||
{
|
||||
"message": "Unknown parameter `from`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
{
|
||||
// Limit not allowed in task deletion/cancelation queries
|
||||
let params = "limit=12";
|
||||
let err = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap_err();
|
||||
snapshot!(format!("{err}"), @"Json deserialize error: unknown field `limit`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.");
|
||||
snapshot!(meili_snap::json_string!(err), @r###"
|
||||
{
|
||||
"message": "Unknown parameter `limit`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_task_delete_or_cancel_empty() {
|
||||
{
|
||||
let params = "";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
assert!(query.is_empty());
|
||||
}
|
||||
{
|
||||
let params = "statuses=*";
|
||||
let query = deserr_query_params::<TaskDeletionOrCancelationQuery>(params).unwrap();
|
||||
assert!(!query.is_empty());
|
||||
snapshot!(format!("{query:?}"), @"TaskDeletionOrCancelationQuery { uids: None, canceled_by: None, types: None, statuses: Star, index_uids: None, after_enqueued_at: None, before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None }");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ use std::time::Instant;
|
|||
|
||||
use deserr::DeserializeFromValue;
|
||||
use either::Either;
|
||||
use meilisearch_types::deserr::DeserrJsonError;
|
||||
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;
|
||||
|
@ -15,7 +15,7 @@ use milli::{
|
|||
SortError, TermsMatchingStrategy, DEFAULT_VALUES_PER_FACET,
|
||||
};
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::Serialize;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::error::MeilisearchHttpError;
|
||||
|
@ -30,41 +30,41 @@ pub const DEFAULT_HIGHLIGHT_PRE_TAG: fn() -> String = || "<em>".to_string();
|
|||
pub const DEFAULT_HIGHLIGHT_POST_TAG: fn() -> String = || "</em>".to_string();
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, DeserializeFromValue)]
|
||||
#[deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields)]
|
||||
#[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)]
|
||||
pub struct SearchQuery {
|
||||
#[deserr(error = DeserrError<InvalidSearchQ>)]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidSearchQ>)]
|
||||
pub q: Option<String>,
|
||||
#[deserr(error = DeserrError<InvalidSearchOffset>, default = DEFAULT_SEARCH_OFFSET())]
|
||||
#[deserr(default = DEFAULT_SEARCH_OFFSET(), error = DeserrJsonError<InvalidSearchOffset>)]
|
||||
pub offset: usize,
|
||||
#[deserr(error = DeserrError<InvalidSearchLimit>, default = DEFAULT_SEARCH_LIMIT())]
|
||||
#[deserr(default = DEFAULT_SEARCH_LIMIT(), error = DeserrJsonError<InvalidSearchLimit>)]
|
||||
pub limit: usize,
|
||||
#[deserr(error = DeserrError<InvalidSearchPage>)]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidSearchPage>)]
|
||||
pub page: Option<usize>,
|
||||
#[deserr(error = DeserrError<InvalidSearchHitsPerPage>)]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidSearchHitsPerPage>)]
|
||||
pub hits_per_page: Option<usize>,
|
||||
#[deserr(error = DeserrError<InvalidSearchAttributesToRetrieve>)]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidSearchAttributesToRetrieve>)]
|
||||
pub attributes_to_retrieve: Option<BTreeSet<String>>,
|
||||
#[deserr(error = DeserrError<InvalidSearchAttributesToCrop>)]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidSearchAttributesToCrop>)]
|
||||
pub attributes_to_crop: Option<Vec<String>>,
|
||||
#[deserr(error = DeserrError<InvalidSearchCropLength>, default = DEFAULT_CROP_LENGTH())]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidSearchCropLength>, default = DEFAULT_CROP_LENGTH())]
|
||||
pub crop_length: usize,
|
||||
#[deserr(error = DeserrError<InvalidSearchAttributesToHighlight>)]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidSearchAttributesToHighlight>)]
|
||||
pub attributes_to_highlight: Option<HashSet<String>>,
|
||||
#[deserr(error = DeserrError<InvalidSearchShowMatchesPosition>, default)]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidSearchShowMatchesPosition>, default)]
|
||||
pub show_matches_position: bool,
|
||||
#[deserr(error = DeserrError<InvalidSearchFilter>)]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidSearchFilter>)]
|
||||
pub filter: Option<Value>,
|
||||
#[deserr(error = DeserrError<InvalidSearchSort>)]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidSearchSort>)]
|
||||
pub sort: Option<Vec<String>>,
|
||||
#[deserr(error = DeserrError<InvalidSearchFacets>)]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidSearchFacets>)]
|
||||
pub facets: Option<Vec<String>>,
|
||||
#[deserr(error = DeserrError<InvalidSearchHighlightPreTag>, default = DEFAULT_HIGHLIGHT_PRE_TAG())]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidSearchHighlightPreTag>, default = DEFAULT_HIGHLIGHT_PRE_TAG())]
|
||||
pub highlight_pre_tag: String,
|
||||
#[deserr(error = DeserrError<InvalidSearchHighlightPostTag>, default = DEFAULT_HIGHLIGHT_POST_TAG())]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidSearchHighlightPostTag>, default = DEFAULT_HIGHLIGHT_POST_TAG())]
|
||||
pub highlight_post_tag: String,
|
||||
#[deserr(error = DeserrError<InvalidSearchCropMarker>, default = DEFAULT_CROP_MARKER())]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidSearchCropMarker>, default = DEFAULT_CROP_MARKER())]
|
||||
pub crop_marker: String,
|
||||
#[deserr(error = DeserrError<InvalidSearchMatchingStrategy>, default)]
|
||||
#[deserr(default, error = DeserrJsonError<InvalidSearchMatchingStrategy>, default)]
|
||||
pub matching_strategy: MatchingStrategy,
|
||||
}
|
||||
|
||||
|
@ -74,9 +74,8 @@ impl SearchQuery {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, PartialEq, Eq, DeserializeFromValue)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, DeserializeFromValue)]
|
||||
#[deserr(rename_all = camelCase)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum MatchingStrategy {
|
||||
/// Remove query words from last to first
|
||||
Last,
|
||||
|
|
|
@ -205,7 +205,7 @@ async fn error_add_api_key_no_header() {
|
|||
"message": "The Authorization header is missing. It must use the bearer authorization method.",
|
||||
"code": "missing_authorization_header",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#missing-authorization-header"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_authorization_header"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ async fn error_add_api_key_bad_key() {
|
|||
"message": "The provided API key is invalid.",
|
||||
"code": "invalid_api_key",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -248,10 +248,10 @@ async fn error_add_api_key_missing_parameter() {
|
|||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: missing field `indexes` at ``",
|
||||
"code": "bad_request",
|
||||
"message": "Missing field `indexes`",
|
||||
"code": "missing_api_key_indexes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_api_key_indexes"
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -265,10 +265,10 @@ async fn error_add_api_key_missing_parameter() {
|
|||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: missing field `actions` at ``",
|
||||
"code": "bad_request",
|
||||
"message": "Missing field `actions`",
|
||||
"code": "missing_api_key_actions",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_api_key_actions"
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -279,22 +279,13 @@ async fn error_add_api_key_missing_parameter() {
|
|||
"actions": ["documents.add"],
|
||||
});
|
||||
let (response, code) = server.add_api_key(content).await;
|
||||
meili_snap::snapshot!(code, @"201 Created");
|
||||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]", ".uid" => "[ignored]", ".key" => "[ignored]" }), @r###"
|
||||
{
|
||||
"name": null,
|
||||
"description": "Indexing API key",
|
||||
"key": "[ignored]",
|
||||
"uid": "[ignored]",
|
||||
"actions": [
|
||||
"documents.add"
|
||||
],
|
||||
"indexes": [
|
||||
"products"
|
||||
],
|
||||
"expiresAt": null,
|
||||
"createdAt": "[ignored]",
|
||||
"updatedAt": "[ignored]"
|
||||
"message": "Missing field `expiresAt`",
|
||||
"code": "missing_api_key_expires_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#missing_api_key_expires_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -314,10 +305,10 @@ async fn error_add_api_key_invalid_parameters_description() {
|
|||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "invalid type: Map `{\"name\":\"products\"}`, expected a String at `.description`.",
|
||||
"message": "Invalid value type at `.description`: expected a string, but found an object: `{\"name\":\"products\"}`",
|
||||
"code": "invalid_api_key_description",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key-description"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_description"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -337,10 +328,10 @@ async fn error_add_api_key_invalid_parameters_name() {
|
|||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "invalid type: Map `{\"name\":\"products\"}`, expected a String at `.name`.",
|
||||
"message": "Invalid value type at `.name`: expected a string, but found an object: `{\"name\":\"products\"}`",
|
||||
"code": "invalid_api_key_name",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key-name"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_name"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -360,10 +351,10 @@ async fn error_add_api_key_invalid_parameters_indexes() {
|
|||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "invalid type: Map `{\"name\":\"products\"}`, expected a Sequence at `.indexes`.",
|
||||
"message": "Invalid value type at `.indexes`: expected an array, but found an object: `{\"name\":\"products\"}`",
|
||||
"code": "invalid_api_key_indexes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key-indexes"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_indexes"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -386,10 +377,10 @@ async fn error_add_api_key_invalid_index_uids() {
|
|||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "`invalid index # / \\name with spaces` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). at `.indexes[0]`.",
|
||||
"message": "Invalid value at `.indexes[0]`: `invalid index # / \\name with spaces` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).",
|
||||
"code": "invalid_api_key_indexes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key-indexes"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_indexes"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -411,10 +402,10 @@ async fn error_add_api_key_invalid_parameters_actions() {
|
|||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "invalid type: Map `{\"name\":\"products\"}`, expected a Sequence at `.actions`.",
|
||||
"message": "Invalid value type at `.actions`: expected an array, but found an object: `{\"name\":\"products\"}`",
|
||||
"code": "invalid_api_key_actions",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key-actions"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_actions"
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -431,10 +422,10 @@ async fn error_add_api_key_invalid_parameters_actions() {
|
|||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: unknown value `doc.add`, expected one of `*`, `search`, `documents.*`, `documents.add`, `documents.get`, `documents.delete`, `indexes.*`, `indexes.create`, `indexes.get`, `indexes.update`, `indexes.delete`, `indexes.swap`, `tasks.*`, `tasks.cancel`, `tasks.delete`, `tasks.get`, `settings.*`, `settings.get`, `settings.update`, `stats.*`, `stats.get`, `metrics.*`, `metrics.get`, `dumps.*`, `dumps.create`, `version`, `keys.create`, `keys.get`, `keys.update`, `keys.delete` at `.actions[0]`.",
|
||||
"message": "Unknown value `doc.add` at `.actions[0]`: expected one of `*`, `search`, `documents.*`, `documents.add`, `documents.get`, `documents.delete`, `indexes.*`, `indexes.create`, `indexes.get`, `indexes.update`, `indexes.delete`, `indexes.swap`, `tasks.*`, `tasks.cancel`, `tasks.delete`, `tasks.get`, `settings.*`, `settings.get`, `settings.update`, `stats.*`, `stats.get`, `metrics.*`, `metrics.get`, `dumps.*`, `dumps.create`, `version`, `keys.create`, `keys.get`, `keys.update`, `keys.delete`",
|
||||
"code": "invalid_api_key_actions",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key-actions"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_actions"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -455,10 +446,10 @@ async fn error_add_api_key_invalid_parameters_expires_at() {
|
|||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "invalid type: Map `{\"name\":\"products\"}`, expected a String at `.expiresAt`.",
|
||||
"message": "Invalid value type at `.expiresAt`: expected a string, but found an object: `{\"name\":\"products\"}`",
|
||||
"code": "invalid_api_key_expires_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key-expires-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_expires_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -478,10 +469,10 @@ async fn error_add_api_key_invalid_parameters_expires_at_in_the_past() {
|
|||
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "`2010-11-13T00:00:00Z` is not a valid date. It should follow the RFC 3339 format to represents a date or datetime in the future or specified as a null value. e.g. 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'.\n at `.expiresAt`.",
|
||||
"message": "Invalid value at `.expiresAt`: `2010-11-13T00:00:00Z` is not a valid date. It should follow the RFC 3339 format to represents a date or datetime in the future or specified as a null value. e.g. 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'.\n",
|
||||
"code": "invalid_api_key_expires_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key-expires-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_expires_at"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
|
@ -503,10 +494,10 @@ async fn error_add_api_key_invalid_parameters_uid() {
|
|||
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "invalid length: expected length 32 for simple format, found 13 at `.uid`.",
|
||||
"message": "Invalid value at `.uid`: invalid length: expected length 32 for simple format, found 13",
|
||||
"code": "invalid_api_key_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key-uid"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_uid"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
|
@ -551,7 +542,7 @@ async fn error_add_api_key_parameters_uid_already_exist() {
|
|||
"message": "`uid` field value `4bc0887a-0e41-4f3b-935d-0c451dcee9c8` is already an existing API key.",
|
||||
"code": "api_key_already_exists",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#api-key-already-exists"
|
||||
"link": "https://docs.meilisearch.com/errors#api_key_already_exists"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"409 Conflict");
|
||||
|
@ -697,7 +688,7 @@ async fn error_get_api_key_no_header() {
|
|||
"message": "The Authorization header is missing. It must use the bearer authorization method.",
|
||||
"code": "missing_authorization_header",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#missing-authorization-header"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_authorization_header"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"401 Unauthorized");
|
||||
|
@ -716,7 +707,7 @@ async fn error_get_api_key_bad_key() {
|
|||
"message": "The provided API key is invalid.",
|
||||
"code": "invalid_api_key",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"403 Forbidden");
|
||||
|
@ -735,7 +726,7 @@ async fn error_get_api_key_not_found() {
|
|||
"message": "API key `d0552b41d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4` not found.",
|
||||
"code": "api_key_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#api-key-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#api_key_not_found"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"404 Not Found");
|
||||
|
@ -799,7 +790,7 @@ async fn list_api_keys() {
|
|||
"###);
|
||||
meili_snap::snapshot!(code, @"201 Created");
|
||||
|
||||
let (response, code) = server.list_api_keys().await;
|
||||
let (response, code) = server.list_api_keys("").await;
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".results[].createdAt" => "[ignored]", ".results[].updatedAt" => "[ignored]", ".results[].uid" => "[ignored]", ".results[].key" => "[ignored]" }), @r###"
|
||||
{
|
||||
"results": [
|
||||
|
@ -873,13 +864,13 @@ async fn list_api_keys() {
|
|||
async fn error_list_api_keys_no_header() {
|
||||
let server = Server::new_auth().await;
|
||||
|
||||
let (response, code) = server.list_api_keys().await;
|
||||
let (response, code) = server.list_api_keys("").await;
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "The Authorization header is missing. It must use the bearer authorization method.",
|
||||
"code": "missing_authorization_header",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#missing-authorization-header"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_authorization_header"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"401 Unauthorized");
|
||||
|
@ -890,13 +881,13 @@ async fn error_list_api_keys_bad_key() {
|
|||
let mut server = Server::new_auth().await;
|
||||
server.use_api_key("d4000bd7225f77d1eb22cc706ed36772bbc36767c016a27f76def7537b68600d");
|
||||
|
||||
let (response, code) = server.list_api_keys().await;
|
||||
let (response, code) = server.list_api_keys("").await;
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "The provided API key is invalid.",
|
||||
"code": "invalid_api_key",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"403 Forbidden");
|
||||
|
@ -973,7 +964,7 @@ async fn delete_api_key() {
|
|||
"message": "[ignored]",
|
||||
"code": "api_key_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#api-key-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#api_key_not_found"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"404 Not Found");
|
||||
|
@ -992,7 +983,7 @@ async fn error_delete_api_key_no_header() {
|
|||
"message": "The Authorization header is missing. It must use the bearer authorization method.",
|
||||
"code": "missing_authorization_header",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#missing-authorization-header"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_authorization_header"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"401 Unauthorized");
|
||||
|
@ -1011,7 +1002,7 @@ async fn error_delete_api_key_bad_key() {
|
|||
"message": "The provided API key is invalid.",
|
||||
"code": "invalid_api_key",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"403 Forbidden");
|
||||
|
@ -1030,7 +1021,7 @@ async fn error_delete_api_key_not_found() {
|
|||
"message": "API key `d0552b41d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4` not found.",
|
||||
"code": "api_key_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#api-key-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#api_key_not_found"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"404 Not Found");
|
||||
|
@ -1089,14 +1080,14 @@ async fn patch_api_key_description() {
|
|||
|
||||
let uid = response["uid"].as_str().unwrap();
|
||||
|
||||
// Add a description
|
||||
let content = json!({ "description": "Indexing API key" });
|
||||
// Add a description and a name
|
||||
let content = json!({ "description": "Indexing API key", "name": "bob" });
|
||||
|
||||
thread::sleep(time::Duration::new(1, 0));
|
||||
let (response, code) = server.patch_api_key(&uid, content).await;
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]", ".uid" => "[ignored]", ".key" => "[ignored]" }), @r###"
|
||||
{
|
||||
"name": null,
|
||||
"name": "bob",
|
||||
"description": "Indexing API key",
|
||||
"key": "[ignored]",
|
||||
"uid": "[ignored]",
|
||||
|
@ -1128,7 +1119,7 @@ async fn patch_api_key_description() {
|
|||
let (response, code) = server.patch_api_key(&uid, content).await;
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]", ".uid" => "[ignored]", ".key" => "[ignored]" }), @r###"
|
||||
{
|
||||
"name": null,
|
||||
"name": "bob",
|
||||
"description": "Product API key",
|
||||
"key": "[ignored]",
|
||||
"uid": "[ignored]",
|
||||
|
@ -1160,7 +1151,7 @@ async fn patch_api_key_description() {
|
|||
let (response, code) = server.patch_api_key(&uid, content).await;
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]", ".uid" => "[ignored]", ".key" => "[ignored]" }), @r###"
|
||||
{
|
||||
"name": null,
|
||||
"name": "bob",
|
||||
"description": null,
|
||||
"key": "[ignored]",
|
||||
"uid": "[ignored]",
|
||||
|
@ -1242,15 +1233,15 @@ async fn patch_api_key_name() {
|
|||
let created_at = response["createdAt"].as_str().unwrap();
|
||||
let updated_at = response["updatedAt"].as_str().unwrap();
|
||||
|
||||
// Add a name
|
||||
let content = json!({ "name": "Indexing API key" });
|
||||
// Add a name and description
|
||||
let content = json!({ "name": "Indexing API key", "description": "The doggoscription" });
|
||||
|
||||
thread::sleep(time::Duration::new(1, 0));
|
||||
let (response, code) = server.patch_api_key(&uid, content).await;
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]", ".uid" => "[ignored]", ".key" => "[ignored]" }), @r###"
|
||||
{
|
||||
"name": "Indexing API key",
|
||||
"description": null,
|
||||
"description": "The doggoscription",
|
||||
"key": "[ignored]",
|
||||
"uid": "[ignored]",
|
||||
"actions": [
|
||||
|
@ -1285,7 +1276,7 @@ async fn patch_api_key_name() {
|
|||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]", ".uid" => "[ignored]", ".key" => "[ignored]" }), @r###"
|
||||
{
|
||||
"name": "Product API key",
|
||||
"description": null,
|
||||
"description": "The doggoscription",
|
||||
"key": "[ignored]",
|
||||
"uid": "[ignored]",
|
||||
"actions": [
|
||||
|
@ -1311,13 +1302,13 @@ async fn patch_api_key_name() {
|
|||
meili_snap::snapshot!(code, @"200 OK");
|
||||
|
||||
// Remove the name
|
||||
let content = json!({ "name": serde_json::Value::Null });
|
||||
let content = json!({ "name": null });
|
||||
|
||||
let (response, code) = server.patch_api_key(&uid, content).await;
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]", ".uid" => "[ignored]", ".key" => "[ignored]" }), @r###"
|
||||
{
|
||||
"name": null,
|
||||
"description": null,
|
||||
"description": "The doggoscription",
|
||||
"key": "[ignored]",
|
||||
"uid": "[ignored]",
|
||||
"actions": [
|
||||
|
@ -1403,10 +1394,10 @@ async fn error_patch_api_key_indexes() {
|
|||
let (response, code) = server.patch_api_key(&uid, content).await;
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: unknown field `indexes`, expected one of `description`, `name` at ``.",
|
||||
"message": "Immutable field `indexes`: expected one of `description`, `name`",
|
||||
"code": "immutable_api_key_indexes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#immutable-api-key-indexes"
|
||||
"link": "https://docs.meilisearch.com/errors#immutable_api_key_indexes"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
|
@ -1480,10 +1471,10 @@ async fn error_patch_api_key_actions() {
|
|||
let (response, code) = server.patch_api_key(&uid, content).await;
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: unknown field `actions`, expected one of `description`, `name` at ``.",
|
||||
"message": "Immutable field `actions`: expected one of `description`, `name`",
|
||||
"code": "immutable_api_key_actions",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#immutable-api-key-actions"
|
||||
"link": "https://docs.meilisearch.com/errors#immutable_api_key_actions"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
|
@ -1549,10 +1540,10 @@ async fn error_patch_api_key_expiration_date() {
|
|||
let (response, code) = server.patch_api_key(&uid, content).await;
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: unknown field `expiresAt`, expected one of `description`, `name` at ``.",
|
||||
"message": "Immutable field `expiresAt`: expected one of `description`, `name`",
|
||||
"code": "immutable_api_key_expires_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#immutable-api-key-expires-at"
|
||||
"link": "https://docs.meilisearch.com/errors#immutable_api_key_expires_at"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
|
@ -1574,7 +1565,7 @@ async fn error_patch_api_key_no_header() {
|
|||
"message": "The Authorization header is missing. It must use the bearer authorization method.",
|
||||
"code": "missing_authorization_header",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#missing-authorization-header"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_authorization_header"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"401 Unauthorized");
|
||||
|
@ -1597,7 +1588,7 @@ async fn error_patch_api_key_bad_key() {
|
|||
"message": "The provided API key is invalid.",
|
||||
"code": "invalid_api_key",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"403 Forbidden");
|
||||
|
@ -1620,7 +1611,7 @@ async fn error_patch_api_key_not_found() {
|
|||
"message": "API key `d0552b41d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4` not found.",
|
||||
"code": "api_key_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#api-key-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#api_key_not_found"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"404 Not Found");
|
||||
|
@ -1670,10 +1661,10 @@ async fn error_patch_api_key_indexes_invalid_parameters() {
|
|||
let (response, code) = server.patch_api_key(&uid, content).await;
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "invalid type: Integer `13`, expected a String at `.description`.",
|
||||
"message": "Invalid value type at `.description`: expected a string, but found a positive integer: `13`",
|
||||
"code": "invalid_api_key_description",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key-description"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_description"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
|
@ -1686,10 +1677,10 @@ async fn error_patch_api_key_indexes_invalid_parameters() {
|
|||
let (response, code) = server.patch_api_key(&uid, content).await;
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "invalid type: Integer `13`, expected a String at `.name`.",
|
||||
"message": "Invalid value type at `.name`: expected a string, but found a positive integer: `13`",
|
||||
"code": "invalid_api_key_name",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key-name"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_name"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"400 Bad Request");
|
||||
|
@ -1705,7 +1696,7 @@ async fn error_access_api_key_routes_no_master_key_set() {
|
|||
"message": "Meilisearch is running without a master key. To access this API endpoint, you must have set a master key at launch.",
|
||||
"code": "missing_master_key",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#missing-master-key"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_master_key"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"401 Unauthorized");
|
||||
|
@ -1716,7 +1707,7 @@ async fn error_access_api_key_routes_no_master_key_set() {
|
|||
"message": "Meilisearch is running without a master key. To access this API endpoint, you must have set a master key at launch.",
|
||||
"code": "missing_master_key",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#missing-master-key"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_master_key"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"401 Unauthorized");
|
||||
|
@ -1727,18 +1718,18 @@ async fn error_access_api_key_routes_no_master_key_set() {
|
|||
"message": "Meilisearch is running without a master key. To access this API endpoint, you must have set a master key at launch.",
|
||||
"code": "missing_master_key",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#missing-master-key"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_master_key"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"401 Unauthorized");
|
||||
|
||||
let (response, code) = server.list_api_keys().await;
|
||||
let (response, code) = server.list_api_keys("").await;
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "Meilisearch is running without a master key. To access this API endpoint, you must have set a master key at launch.",
|
||||
"code": "missing_master_key",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#missing-master-key"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_master_key"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"401 Unauthorized");
|
||||
|
@ -1751,7 +1742,7 @@ async fn error_access_api_key_routes_no_master_key_set() {
|
|||
"message": "Meilisearch is running without a master key. To access this API endpoint, you must have set a master key at launch.",
|
||||
"code": "missing_master_key",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#missing-master-key"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_master_key"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"401 Unauthorized");
|
||||
|
@ -1762,7 +1753,7 @@ async fn error_access_api_key_routes_no_master_key_set() {
|
|||
"message": "Meilisearch is running without a master key. To access this API endpoint, you must have set a master key at launch.",
|
||||
"code": "missing_master_key",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#missing-master-key"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_master_key"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"401 Unauthorized");
|
||||
|
@ -1773,18 +1764,18 @@ async fn error_access_api_key_routes_no_master_key_set() {
|
|||
"message": "Meilisearch is running without a master key. To access this API endpoint, you must have set a master key at launch.",
|
||||
"code": "missing_master_key",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#missing-master-key"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_master_key"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"401 Unauthorized");
|
||||
|
||||
let (response, code) = server.list_api_keys().await;
|
||||
let (response, code) = server.list_api_keys("").await;
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response, { ".createdAt" => "[ignored]", ".updatedAt" => "[ignored]" }), @r###"
|
||||
{
|
||||
"message": "Meilisearch is running without a master key. To access this API endpoint, you must have set a master key at launch.",
|
||||
"code": "missing_master_key",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#missing-master-key"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_master_key"
|
||||
}
|
||||
"###);
|
||||
meili_snap::snapshot!(code, @"401 Unauthorized");
|
||||
|
|
|
@ -73,7 +73,7 @@ static INVALID_RESPONSE: Lazy<Value> = Lazy::new(|| {
|
|||
json!({"message": "The provided API key is invalid.",
|
||||
"code": "invalid_api_key",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key"
|
||||
})
|
||||
});
|
||||
|
||||
|
@ -520,7 +520,7 @@ async fn error_creating_index_without_action() {
|
|||
"message": "Index `test` not found.",
|
||||
"code": "index_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#index_not_found"
|
||||
});
|
||||
|
||||
// try to create a index via add documents route
|
||||
|
|
438
meilisearch/tests/auth/errors.rs
Normal file
438
meilisearch/tests/auth/errors.rs
Normal file
|
@ -0,0 +1,438 @@
|
|||
use meili_snap::*;
|
||||
use serde_json::json;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::common::Server;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn create_api_key_bad_description() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server.add_api_key(json!({ "description": ["doggo"] })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value type at `.description`: expected a string, but found an array: `[\"doggo\"]`",
|
||||
"code": "invalid_api_key_description",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_description"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn create_api_key_bad_name() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server.add_api_key(json!({ "name": ["doggo"] })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value type at `.name`: expected a string, but found an array: `[\"doggo\"]`",
|
||||
"code": "invalid_api_key_name",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_name"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn create_api_key_bad_uid() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
// bad type
|
||||
let (response, code) = server.add_api_key(json!({ "uid": ["doggo"] })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value type at `.uid`: expected a string, but found an array: `[\"doggo\"]`",
|
||||
"code": "invalid_api_key_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_uid"
|
||||
}
|
||||
"###);
|
||||
|
||||
// can't parse
|
||||
let (response, code) = server.add_api_key(json!({ "uid": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value at `.uid`: invalid character: expected an optional prefix of `urn:uuid:` followed by [0-9a-zA-Z], found `o` at 2",
|
||||
"code": "invalid_api_key_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_uid"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn create_api_key_bad_actions() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
// bad type
|
||||
let (response, code) = server.add_api_key(json!({ "actions": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value type at `.actions`: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_api_key_actions",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_actions"
|
||||
}
|
||||
"###);
|
||||
|
||||
// can't parse
|
||||
let (response, code) = server.add_api_key(json!({ "actions": ["doggo"] })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Unknown value `doggo` at `.actions[0]`: expected one of `*`, `search`, `documents.*`, `documents.add`, `documents.get`, `documents.delete`, `indexes.*`, `indexes.create`, `indexes.get`, `indexes.update`, `indexes.delete`, `indexes.swap`, `tasks.*`, `tasks.cancel`, `tasks.delete`, `tasks.get`, `settings.*`, `settings.get`, `settings.update`, `stats.*`, `stats.get`, `metrics.*`, `metrics.get`, `dumps.*`, `dumps.create`, `version`, `keys.create`, `keys.get`, `keys.update`, `keys.delete`",
|
||||
"code": "invalid_api_key_actions",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_actions"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn create_api_key_bad_indexes() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
// bad type
|
||||
let (response, code) = server.add_api_key(json!({ "indexes": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value type at `.indexes`: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_api_key_indexes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_indexes"
|
||||
}
|
||||
"###);
|
||||
|
||||
// can't parse
|
||||
let (response, code) = server.add_api_key(json!({ "indexes": ["good doggo"] })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value at `.indexes[0]`: `good doggo` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).",
|
||||
"code": "invalid_api_key_indexes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_indexes"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn create_api_key_bad_expires_at() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
// bad type
|
||||
let (response, code) = server.add_api_key(json!({ "expires_at": ["doggo"] })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Unknown field `expires_at`: expected one of `description`, `name`, `uid`, `actions`, `indexes`, `expiresAt`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
|
||||
// can't parse
|
||||
let (response, code) = server.add_api_key(json!({ "expires_at": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Unknown field `expires_at`: expected one of `description`, `name`, `uid`, `actions`, `indexes`, `expiresAt`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn create_api_key_missing_action() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) =
|
||||
server.add_api_key(json!({ "indexes": ["doggo"], "expiresAt": null })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Missing field `actions`",
|
||||
"code": "missing_api_key_actions",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#missing_api_key_actions"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn create_api_key_missing_indexes() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server
|
||||
.add_api_key(json!({ "uid": Uuid::nil() , "actions": ["*"], "expiresAt": null }))
|
||||
.await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Missing field `indexes`",
|
||||
"code": "missing_api_key_indexes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#missing_api_key_indexes"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn create_api_key_missing_expires_at() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server
|
||||
.add_api_key(json!({ "uid": Uuid::nil(), "actions": ["*"], "indexes": ["doggo"] }))
|
||||
.await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Missing field `expiresAt`",
|
||||
"code": "missing_api_key_expires_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#missing_api_key_expires_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn create_api_key_unexpected_field() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server
|
||||
.add_api_key(json!({ "uid": Uuid::nil(), "actions": ["*"], "indexes": ["doggo"], "expiresAt": null, "doggo": "bork" }))
|
||||
.await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Unknown field `doggo`: expected one of `description`, `name`, `uid`, `actions`, `indexes`, `expiresAt`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn list_api_keys_bad_offset() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server.list_api_keys("?offset=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `offset`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_api_key_offset",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_offset"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn list_api_keys_bad_limit() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server.list_api_keys("?limit=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `limit`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_api_key_limit",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_limit"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn list_api_keys_unexpected_field() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server.list_api_keys("?doggo=no_limit").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Unknown parameter `doggo`: expected one of `offset`, `limit`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn patch_api_keys_bad_description() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server.patch_api_key("doggo", json!({ "description": ["doggo"] })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value type at `.description`: expected a string, but found an array: `[\"doggo\"]`",
|
||||
"code": "invalid_api_key_description",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_description"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn patch_api_keys_bad_name() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server.patch_api_key("doggo", json!({ "name": ["doggo"] })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value type at `.name`: expected a string, but found an array: `[\"doggo\"]`",
|
||||
"code": "invalid_api_key_name",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key_name"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn patch_api_keys_immutable_uid() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server.patch_api_key("doggo", json!({ "uid": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Immutable field `uid`: expected one of `description`, `name`",
|
||||
"code": "immutable_api_key_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#immutable_api_key_uid"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn patch_api_keys_immutable_actions() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server.patch_api_key("doggo", json!({ "actions": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Immutable field `actions`: expected one of `description`, `name`",
|
||||
"code": "immutable_api_key_actions",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#immutable_api_key_actions"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn patch_api_keys_immutable_indexes() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server.patch_api_key("doggo", json!({ "indexes": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Immutable field `indexes`: expected one of `description`, `name`",
|
||||
"code": "immutable_api_key_indexes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#immutable_api_key_indexes"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn patch_api_keys_immutable_expires_at() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server.patch_api_key("doggo", json!({ "expiresAt": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Immutable field `expiresAt`: expected one of `description`, `name`",
|
||||
"code": "immutable_api_key_expires_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#immutable_api_key_expires_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn patch_api_keys_immutable_created_at() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server.patch_api_key("doggo", json!({ "createdAt": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Immutable field `createdAt`: expected one of `description`, `name`",
|
||||
"code": "immutable_api_key_created_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#immutable_api_key_created_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn patch_api_keys_immutable_updated_at() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server.patch_api_key("doggo", json!({ "updatedAt": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Immutable field `updatedAt`: expected one of `description`, `name`",
|
||||
"code": "immutable_api_key_updated_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#immutable_api_key_updated_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn patch_api_keys_unknown_field() {
|
||||
let mut server = Server::new_auth().await;
|
||||
server.use_admin_key("MASTER_KEY").await;
|
||||
|
||||
let (response, code) = server.patch_api_key("doggo", json!({ "doggo": "bork" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Unknown field `doggo`: expected one of `description`, `name`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
mod api_keys;
|
||||
mod authorization;
|
||||
mod errors;
|
||||
mod payload;
|
||||
mod tenant_token;
|
||||
|
||||
|
@ -16,7 +17,7 @@ impl Server {
|
|||
/// Fetch and use the default admin key for nexts http requests.
|
||||
pub async fn use_admin_key(&mut self, master_key: impl AsRef<str>) {
|
||||
self.use_api_key(master_key);
|
||||
let (response, code) = self.list_api_keys().await;
|
||||
let (response, code) = self.list_api_keys("").await;
|
||||
assert_eq!(200, code, "{:?}", response);
|
||||
let admin_key = &response["results"][1]["key"];
|
||||
self.use_api_key(admin_key.as_str().unwrap());
|
||||
|
@ -37,8 +38,8 @@ impl Server {
|
|||
self.service.patch(url, content).await
|
||||
}
|
||||
|
||||
pub async fn list_api_keys(&self) -> (Value, StatusCode) {
|
||||
let url = "/keys";
|
||||
pub async fn list_api_keys(&self, params: &str) -> (Value, StatusCode) {
|
||||
let url = format!("/keys{params}");
|
||||
self.service.get(url).await
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ async fn error_api_key_bad_content_types() {
|
|||
);
|
||||
assert_eq!(response["code"], "invalid_content_type");
|
||||
assert_eq!(response["type"], "invalid_request");
|
||||
assert_eq!(response["link"], "https://docs.meilisearch.com/errors#invalid-content-type");
|
||||
assert_eq!(response["link"], "https://docs.meilisearch.com/errors#invalid_content_type");
|
||||
|
||||
// patch
|
||||
let req = test::TestRequest::patch()
|
||||
|
@ -59,7 +59,7 @@ async fn error_api_key_bad_content_types() {
|
|||
);
|
||||
assert_eq!(response["code"], "invalid_content_type");
|
||||
assert_eq!(response["type"], "invalid_request");
|
||||
assert_eq!(response["link"], "https://docs.meilisearch.com/errors#invalid-content-type");
|
||||
assert_eq!(response["link"], "https://docs.meilisearch.com/errors#invalid_content_type");
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
|
@ -96,7 +96,7 @@ async fn error_api_key_empty_content_types() {
|
|||
);
|
||||
assert_eq!(response["code"], "invalid_content_type");
|
||||
assert_eq!(response["type"], "invalid_request");
|
||||
assert_eq!(response["link"], "https://docs.meilisearch.com/errors#invalid-content-type");
|
||||
assert_eq!(response["link"], "https://docs.meilisearch.com/errors#invalid_content_type");
|
||||
|
||||
// patch
|
||||
let req = test::TestRequest::patch()
|
||||
|
@ -118,7 +118,7 @@ async fn error_api_key_empty_content_types() {
|
|||
);
|
||||
assert_eq!(response["code"], "invalid_content_type");
|
||||
assert_eq!(response["type"], "invalid_request");
|
||||
assert_eq!(response["link"], "https://docs.meilisearch.com/errors#invalid-content-type");
|
||||
assert_eq!(response["link"], "https://docs.meilisearch.com/errors#invalid_content_type");
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
|
@ -154,7 +154,7 @@ async fn error_api_key_missing_content_types() {
|
|||
);
|
||||
assert_eq!(response["code"], "missing_content_type");
|
||||
assert_eq!(response["type"], "invalid_request");
|
||||
assert_eq!(response["link"], "https://docs.meilisearch.com/errors#missing-content-type");
|
||||
assert_eq!(response["link"], "https://docs.meilisearch.com/errors#missing_content_type");
|
||||
|
||||
// patch
|
||||
let req = test::TestRequest::patch()
|
||||
|
@ -175,7 +175,7 @@ async fn error_api_key_missing_content_types() {
|
|||
);
|
||||
assert_eq!(response["code"], "missing_content_type");
|
||||
assert_eq!(response["type"], "invalid_request");
|
||||
assert_eq!(response["link"], "https://docs.meilisearch.com/errors#missing-content-type");
|
||||
assert_eq!(response["link"], "https://docs.meilisearch.com/errors#missing_content_type");
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
|
@ -200,7 +200,7 @@ async fn error_api_key_empty_payload() {
|
|||
assert_eq!(status_code, 400);
|
||||
assert_eq!(response["code"], json!("missing_payload"));
|
||||
assert_eq!(response["type"], json!("invalid_request"));
|
||||
assert_eq!(response["link"], json!("https://docs.meilisearch.com/errors#missing-payload"));
|
||||
assert_eq!(response["link"], json!("https://docs.meilisearch.com/errors#missing_payload"));
|
||||
assert_eq!(response["message"], json!(r#"A json payload is missing."#));
|
||||
|
||||
// patch
|
||||
|
@ -217,7 +217,7 @@ async fn error_api_key_empty_payload() {
|
|||
assert_eq!(status_code, 400);
|
||||
assert_eq!(response["code"], json!("missing_payload"));
|
||||
assert_eq!(response["type"], json!("invalid_request"));
|
||||
assert_eq!(response["link"], json!("https://docs.meilisearch.com/errors#missing-payload"));
|
||||
assert_eq!(response["link"], json!("https://docs.meilisearch.com/errors#missing_payload"));
|
||||
assert_eq!(response["message"], json!(r#"A json payload is missing."#));
|
||||
}
|
||||
|
||||
|
@ -243,7 +243,7 @@ async fn error_api_key_malformed_payload() {
|
|||
assert_eq!(status_code, 400);
|
||||
assert_eq!(response["code"], json!("malformed_payload"));
|
||||
assert_eq!(response["type"], json!("invalid_request"));
|
||||
assert_eq!(response["link"], json!("https://docs.meilisearch.com/errors#malformed-payload"));
|
||||
assert_eq!(response["link"], json!("https://docs.meilisearch.com/errors#malformed_payload"));
|
||||
assert_eq!(
|
||||
response["message"],
|
||||
json!(
|
||||
|
@ -265,7 +265,7 @@ async fn error_api_key_malformed_payload() {
|
|||
assert_eq!(status_code, 400);
|
||||
assert_eq!(response["code"], json!("malformed_payload"));
|
||||
assert_eq!(response["type"], json!("invalid_request"));
|
||||
assert_eq!(response["link"], json!("https://docs.meilisearch.com/errors#malformed-payload"));
|
||||
assert_eq!(response["link"], json!("https://docs.meilisearch.com/errors#malformed_payload"));
|
||||
assert_eq!(
|
||||
response["message"],
|
||||
json!(
|
||||
|
|
|
@ -56,7 +56,7 @@ static INVALID_RESPONSE: Lazy<Value> = Lazy::new(|| {
|
|||
json!({"message": "The provided API key is invalid.",
|
||||
"code": "invalid_api_key",
|
||||
"type": "auth",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-api-key"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_api_key"
|
||||
})
|
||||
});
|
||||
|
||||
|
|
|
@ -63,6 +63,11 @@ impl Index<'_> {
|
|||
self.service.post_encoded("/indexes", body, self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn update_raw(&self, body: Value) -> (Value, StatusCode) {
|
||||
let url = format!("/indexes/{}", urlencode(self.uid.as_ref()));
|
||||
self.service.patch_encoded(url, body, self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn update(&self, primary_key: Option<&str>) -> (Value, StatusCode) {
|
||||
let body = json!({
|
||||
"primaryKey": primary_key,
|
||||
|
@ -132,7 +137,12 @@ impl Index<'_> {
|
|||
self.service.get(url).await
|
||||
}
|
||||
|
||||
pub async fn filtered_tasks(&self, types: &[&str], statuses: &[&str]) -> (Value, StatusCode) {
|
||||
pub async fn filtered_tasks(
|
||||
&self,
|
||||
types: &[&str],
|
||||
statuses: &[&str],
|
||||
canceled_by: &[&str],
|
||||
) -> (Value, StatusCode) {
|
||||
let mut url = format!("/tasks?indexUids={}", self.uid);
|
||||
if !types.is_empty() {
|
||||
let _ = write!(url, "&types={}", types.join(","));
|
||||
|
@ -140,6 +150,9 @@ impl Index<'_> {
|
|||
if !statuses.is_empty() {
|
||||
let _ = write!(url, "&statuses={}", statuses.join(","));
|
||||
}
|
||||
if !canceled_by.is_empty() {
|
||||
let _ = write!(url, "&canceledBy={}", canceled_by.join(","));
|
||||
}
|
||||
self.service.get(url).await
|
||||
}
|
||||
|
||||
|
@ -155,6 +168,11 @@ impl Index<'_> {
|
|||
self.service.get(url).await
|
||||
}
|
||||
|
||||
pub async fn get_all_documents_raw(&self, options: &str) -> (Value, StatusCode) {
|
||||
let url = format!("/indexes/{}/documents{}", urlencode(self.uid.as_ref()), options);
|
||||
self.service.get(url).await
|
||||
}
|
||||
|
||||
pub async fn get_all_documents(&self, options: GetAllDocumentsOptions) -> (Value, StatusCode) {
|
||||
let mut url = format!("/indexes/{}/documents?", urlencode(self.uid.as_ref()));
|
||||
if let Some(limit) = options.limit {
|
||||
|
@ -187,6 +205,11 @@ impl Index<'_> {
|
|||
self.service.post_encoded(url, serde_json::to_value(&ids).unwrap(), self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn delete_batch_raw(&self, body: Value) -> (Value, StatusCode) {
|
||||
let url = format!("/indexes/{}/documents/delete-batch", urlencode(self.uid.as_ref()));
|
||||
self.service.post_encoded(url, body, self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn settings(&self) -> (Value, StatusCode) {
|
||||
let url = format!("/indexes/{}/settings", urlencode(self.uid.as_ref()));
|
||||
self.service.get(url).await
|
||||
|
@ -289,8 +312,8 @@ impl Index<'_> {
|
|||
eprintln!("Error with post search");
|
||||
resume_unwind(e);
|
||||
}
|
||||
|
||||
let (response, code) = self.search_get(query).await;
|
||||
let query = yaup::to_string(&query).unwrap();
|
||||
let (response, code) = self.search_get(&query).await;
|
||||
if let Err(e) = catch_unwind(move || test(response, code)) {
|
||||
eprintln!("Error with get search");
|
||||
resume_unwind(e);
|
||||
|
@ -302,9 +325,8 @@ impl Index<'_> {
|
|||
self.service.post_encoded(url, query, self.encoder).await
|
||||
}
|
||||
|
||||
pub async fn search_get(&self, query: Value) -> (Value, StatusCode) {
|
||||
let params = yaup::to_string(&query).unwrap();
|
||||
let url = format!("/indexes/{}/search?{}", urlencode(self.uid.as_ref()), params);
|
||||
pub async fn search_get(&self, query: &str) -> (Value, StatusCode) {
|
||||
let url = format!("/indexes/{}/search?{}", urlencode(self.uid.as_ref()), query);
|
||||
self.service.get(url).await
|
||||
}
|
||||
|
||||
|
|
|
@ -95,10 +95,18 @@ impl Server {
|
|||
self.index_with_encoder(uid, Encoder::Plain)
|
||||
}
|
||||
|
||||
pub async fn create_index(&self, body: Value) -> (Value, StatusCode) {
|
||||
self.service.post("/indexes", body).await
|
||||
}
|
||||
|
||||
pub fn index_with_encoder(&self, uid: impl AsRef<str>, encoder: Encoder) -> Index<'_> {
|
||||
Index { uid: uid.as_ref().to_string(), service: &self.service, encoder }
|
||||
}
|
||||
|
||||
pub async fn list_indexes_raw(&self, parameters: &str) -> (Value, StatusCode) {
|
||||
self.service.get(format!("/indexes{parameters}")).await
|
||||
}
|
||||
|
||||
pub async fn list_indexes(
|
||||
&self,
|
||||
offset: Option<usize>,
|
||||
|
@ -132,8 +140,8 @@ impl Server {
|
|||
self.service.get("/tasks").await
|
||||
}
|
||||
|
||||
pub async fn tasks_filter(&self, filter: Value) -> (Value, StatusCode) {
|
||||
self.service.get(format!("/tasks?{}", yaup::to_string(&filter).unwrap())).await
|
||||
pub async fn tasks_filter(&self, filter: &str) -> (Value, StatusCode) {
|
||||
self.service.get(format!("/tasks?{}", filter)).await
|
||||
}
|
||||
|
||||
pub async fn get_dump_status(&self, uid: &str) -> (Value, StatusCode) {
|
||||
|
@ -148,14 +156,12 @@ impl Server {
|
|||
self.service.post("/swap-indexes", value).await
|
||||
}
|
||||
|
||||
pub async fn cancel_tasks(&self, value: Value) -> (Value, StatusCode) {
|
||||
self.service
|
||||
.post(format!("/tasks/cancel?{}", yaup::to_string(&value).unwrap()), json!(null))
|
||||
.await
|
||||
pub async fn cancel_tasks(&self, value: &str) -> (Value, StatusCode) {
|
||||
self.service.post(format!("/tasks/cancel?{}", value), json!(null)).await
|
||||
}
|
||||
|
||||
pub async fn delete_tasks(&self, value: Value) -> (Value, StatusCode) {
|
||||
self.service.delete(format!("/tasks?{}", yaup::to_string(&value).unwrap())).await
|
||||
pub async fn delete_tasks(&self, value: &str) -> (Value, StatusCode) {
|
||||
self.service.delete(format!("/tasks?{}", value)).await
|
||||
}
|
||||
|
||||
pub async fn wait_task(&self, update_id: u64) -> Value {
|
||||
|
|
|
@ -88,7 +88,7 @@ async fn error_json_bad_content_type() {
|
|||
"message": r#"A Content-Type header is missing. Accepted values for the Content-Type header are: `application/json`"#,
|
||||
"code": "missing_content_type",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#missing-content-type",
|
||||
"link": "https://docs.meilisearch.com/errors#missing_content_type",
|
||||
}),
|
||||
"when calling the route `{}` with no content-type",
|
||||
route,
|
||||
|
@ -117,7 +117,7 @@ async fn error_json_bad_content_type() {
|
|||
"message": expected_error_message,
|
||||
"code": "invalid_content_type",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-content-type",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_content_type",
|
||||
}),
|
||||
"when calling the route `{}` with a content-type of `{}`",
|
||||
route,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -95,7 +95,7 @@ async fn error_delete_batch_unexisting_index() {
|
|||
"message": "Index `test` not found.",
|
||||
"code": "index_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#index_not_found"
|
||||
});
|
||||
assert_eq!(code, 202);
|
||||
|
||||
|
|
99
meilisearch/tests/documents/errors.rs
Normal file
99
meilisearch/tests/documents/errors.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
use meili_snap::*;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::Server;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn get_all_documents_bad_offset() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.get_all_documents_raw("?offset").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `offset`: could not parse `` as a positive integer",
|
||||
"code": "invalid_document_offset",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_document_offset"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.get_all_documents_raw("?offset=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `offset`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_document_offset",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_document_offset"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.get_all_documents_raw("?offset=-1").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `offset`: could not parse `-1` as a positive integer",
|
||||
"code": "invalid_document_offset",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_document_offset"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn get_all_documents_bad_limit() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.get_all_documents_raw("?limit").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `limit`: could not parse `` as a positive integer",
|
||||
"code": "invalid_document_limit",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_document_limit"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.get_all_documents_raw("?limit=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `limit`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_document_limit",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_document_limit"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.get_all_documents_raw("?limit=-1").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `limit`: could not parse `-1` as a positive integer",
|
||||
"code": "invalid_document_limit",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_document_limit"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn delete_documents_batch() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
|
||||
let (response, code) = index.delete_batch_raw(json!("doggo")).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: invalid type: string \"doggo\", expected a sequence at line 1 column 7",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
}
|
|
@ -27,7 +27,7 @@ async fn error_get_unexisting_document() {
|
|||
"message": "Document `1` not found.",
|
||||
"code": "document_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#document-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#document_not_found"
|
||||
});
|
||||
|
||||
assert_eq!(response, expected_response);
|
||||
|
@ -90,7 +90,7 @@ async fn error_get_unexisting_index_all_documents() {
|
|||
"message": "Index `test` not found.",
|
||||
"code": "index_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#index_not_found"
|
||||
});
|
||||
|
||||
assert_eq!(response, expected_response);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
mod add_documents;
|
||||
mod delete_documents;
|
||||
mod errors;
|
||||
mod get_documents;
|
||||
mod update_documents;
|
||||
|
|
|
@ -13,7 +13,7 @@ async fn error_document_update_create_index_bad_uid() {
|
|||
"message": "`883 fj!` 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"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"
|
||||
});
|
||||
|
||||
assert_eq!(code, 400);
|
||||
|
@ -167,7 +167,7 @@ async fn error_update_documents_bad_document_id() {
|
|||
assert_eq!(response["error"]["type"], json!("invalid_request"));
|
||||
assert_eq!(
|
||||
response["error"]["link"],
|
||||
json!("https://docs.meilisearch.com/errors#invalid-document-id")
|
||||
json!("https://docs.meilisearch.com/errors#invalid_document_id")
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -193,6 +193,6 @@ async fn error_update_documents_missing_document_id() {
|
|||
assert_eq!(response["error"]["type"], "invalid_request");
|
||||
assert_eq!(
|
||||
response["error"]["link"],
|
||||
"https://docs.meilisearch.com/errors#missing-document-id"
|
||||
"https://docs.meilisearch.com/errors#missing_document_id"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -98,14 +98,14 @@ async fn import_dump_v1_movie_with_settings() {
|
|||
assert_eq!(code, 200);
|
||||
assert_eq!(
|
||||
settings,
|
||||
json!({ "displayedAttributes": ["genres", "id", "overview", "poster", "release_date", "title"], "searchableAttributes": ["title", "overview"], "filterableAttributes": ["genres"], "sortableAttributes": [], "rankingRules": ["typo", "words", "proximity", "attribute", "exactness"], "stopWords": ["of", "the"], "synonyms": {}, "distinctAttribute": null, "typoTolerance": {"enabled": true, "minWordSizeForTypos": { "oneTypo": 5, "twoTypos": 9 }, "disableOnWords": [], "disableOnAttributes": [] }, "faceting": { "maxValuesPerFacet": 100 }, "pagination": { "maxTotalHits": 1000 } })
|
||||
json!({ "displayedAttributes": ["genres", "id", "overview", "poster", "release_date", "title"], "searchableAttributes": ["title", "overview"], "filterableAttributes": ["genres"], "sortableAttributes": ["genres"], "rankingRules": ["typo", "words", "proximity", "attribute", "exactness"], "stopWords": ["of", "the"], "synonyms": {}, "distinctAttribute": null, "typoTolerance": {"enabled": true, "minWordSizeForTypos": { "oneTypo": 5, "twoTypos": 9 }, "disableOnWords": [], "disableOnAttributes": [] }, "faceting": { "maxValuesPerFacet": 100 }, "pagination": { "maxTotalHits": 1000 } })
|
||||
);
|
||||
|
||||
let (tasks, code) = index.list_tasks().await;
|
||||
assert_eq!(code, 200);
|
||||
assert_eq!(
|
||||
tasks,
|
||||
json!({ "results": [{ "uid": 1, "indexUid": "indexUID", "status": "succeeded", "type": "settingsUpdate", "canceledBy": null, "details": { "displayedAttributes": ["genres", "id", "overview", "poster", "release_date", "title"], "searchableAttributes": ["title", "overview"], "filterableAttributes": ["genres"], "stopWords": ["of", "the"] }, "error": null, "duration": "PT7.288826907S", "enqueuedAt": "2021-09-08T09:34:40.882977Z", "startedAt": "2021-09-08T09:34:40.883073093Z", "finishedAt": "2021-09-08T09:34:48.1719Z"}, { "uid": 0, "indexUid": "indexUID", "status": "succeeded", "type": "documentAdditionOrUpdate", "canceledBy": null, "details": { "receivedDocuments": 0, "indexedDocuments": 31968 }, "error": null, "duration": "PT9.090735774S", "enqueuedAt": "2021-09-08T09:34:16.036101Z", "startedAt": "2021-09-08T09:34:16.261191226Z", "finishedAt": "2021-09-08T09:34:25.351927Z" }], "limit": 20, "from": 1, "next": null })
|
||||
json!({ "results": [{ "uid": 1, "indexUid": "indexUID", "status": "succeeded", "type": "settingsUpdate", "canceledBy": null, "details": { "displayedAttributes": ["genres", "id", "overview", "poster", "release_date", "title"], "searchableAttributes": ["title", "overview"], "filterableAttributes": ["genres"], "sortableAttributes": ["genres"], "stopWords": ["of", "the"] }, "error": null, "duration": "PT7.288826907S", "enqueuedAt": "2021-09-08T09:34:40.882977Z", "startedAt": "2021-09-08T09:34:40.883073093Z", "finishedAt": "2021-09-08T09:34:48.1719Z"}, { "uid": 0, "indexUid": "indexUID", "status": "succeeded", "type": "documentAdditionOrUpdate", "canceledBy": null, "details": { "receivedDocuments": 0, "indexedDocuments": 31968 }, "error": null, "duration": "PT9.090735774S", "enqueuedAt": "2021-09-08T09:34:16.036101Z", "startedAt": "2021-09-08T09:34:16.261191226Z", "finishedAt": "2021-09-08T09:34:25.351927Z" }], "limit": 20, "from": 1, "next": null })
|
||||
);
|
||||
|
||||
// finally we're just going to check that we can still get a few documents by id
|
||||
|
@ -161,7 +161,7 @@ async fn import_dump_v1_rubygems_with_settings() {
|
|||
assert_eq!(code, 200);
|
||||
assert_eq!(
|
||||
settings,
|
||||
json!({"displayedAttributes": ["description", "id", "name", "summary", "total_downloads", "version"], "searchableAttributes": ["name", "summary"], "filterableAttributes": ["version"], "sortableAttributes": [], "rankingRules": ["typo", "words", "fame:desc", "proximity", "attribute", "exactness", "total_downloads:desc"], "stopWords": [], "synonyms": {}, "distinctAttribute": null, "typoTolerance": {"enabled": true, "minWordSizeForTypos": {"oneTypo": 5, "twoTypos": 9}, "disableOnWords": [], "disableOnAttributes": [] }, "faceting": { "maxValuesPerFacet": 100 }, "pagination": { "maxTotalHits": 1000 }})
|
||||
json!({"displayedAttributes": ["description", "id", "name", "summary", "total_downloads", "version"], "searchableAttributes": ["name", "summary"], "filterableAttributes": ["version"], "sortableAttributes": ["version"], "rankingRules": ["typo", "words", "fame:desc", "proximity", "attribute", "exactness", "total_downloads:desc"], "stopWords": [], "synonyms": {}, "distinctAttribute": null, "typoTolerance": {"enabled": true, "minWordSizeForTypos": {"oneTypo": 5, "twoTypos": 9}, "disableOnWords": [], "disableOnAttributes": [] }, "faceting": { "maxValuesPerFacet": 100 }, "pagination": { "maxTotalHits": 1000 }})
|
||||
);
|
||||
|
||||
let (tasks, code) = index.list_tasks().await;
|
||||
|
@ -811,7 +811,7 @@ async fn import_dump_v5() {
|
|||
assert_eq!(code, 200);
|
||||
assert_eq!(stats, expected_stats);
|
||||
|
||||
let (keys, code) = server.list_api_keys().await;
|
||||
let (keys, code) = server.list_api_keys("").await;
|
||||
assert_eq!(code, 200);
|
||||
let key = &keys["results"][0];
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use actix_web::http::header::ContentType;
|
||||
use actix_web::test;
|
||||
use http::header::ACCEPT_ENCODING;
|
||||
use meili_snap::{json_string, snapshot};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::common::encoder::Encoder;
|
||||
|
@ -176,7 +177,7 @@ async fn error_create_existing_index() {
|
|||
"message": "Index `test` already exists.",
|
||||
"code": "index_already_exists",
|
||||
"type": "invalid_request",
|
||||
"link":"https://docs.meilisearch.com/errors#index-already-exists"
|
||||
"link":"https://docs.meilisearch.com/errors#index_already_exists"
|
||||
});
|
||||
|
||||
assert_eq!(response["error"], expected_response);
|
||||
|
@ -188,13 +189,13 @@ async fn error_create_with_invalid_index_uid() {
|
|||
let index = server.index("test test#!");
|
||||
let (response, code) = index.create(None).await;
|
||||
|
||||
let expected_response = json!({
|
||||
"message": "`test 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_response);
|
||||
assert_eq!(code, 400);
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value at `.uid`: `test 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"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ async fn error_delete_unexisting_index() {
|
|||
"message": "Index `test` not found.",
|
||||
"code": "index_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#index_not_found"
|
||||
});
|
||||
|
||||
let response = index.wait_task(0).await;
|
||||
|
|
265
meilisearch/tests/index/errors.rs
Normal file
265
meilisearch/tests/index/errors.rs
Normal file
|
@ -0,0 +1,265 @@
|
|||
use meili_snap::*;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::Server;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn get_indexes_bad_offset() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.list_indexes_raw("?offset=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `offset`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_index_offset",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_index_offset"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn get_indexes_bad_limit() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.list_indexes_raw("?limit=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `limit`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_index_limit",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_index_limit"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn get_indexes_unknown_field() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.list_indexes_raw("?doggo=nolimit").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Unknown parameter `doggo`: expected one of `offset`, `limit`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn create_index_missing_uid() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.create_index(json!({ "primaryKey": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Missing field `uid`",
|
||||
"code": "missing_index_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#missing_index_uid"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn create_index_bad_uid() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.create_index(json!({ "uid": "the best doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value at `.uid`: `the best doggo` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).",
|
||||
"code": "invalid_index_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.create_index(json!({ "uid": true })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value type at `.uid`: expected a string, but found a boolean: `true`",
|
||||
"code": "invalid_index_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn create_index_bad_primary_key() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server
|
||||
.create_index(json!({ "uid": "doggo", "primaryKey": ["the", "best", "doggo"] }))
|
||||
.await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value type at `.primaryKey`: expected a string, but found an array: `[\"the\",\"best\",\"doggo\"]`",
|
||||
"code": "invalid_index_primary_key",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_index_primary_key"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn create_index_unknown_field() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.create_index(json!({ "uid": "doggo", "doggo": "bernese" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Unknown field `doggo`: expected one of `uid`, `primaryKey`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn get_index_bad_uid() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("the good doggo");
|
||||
|
||||
let (response, code) = index.get().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 (_).",
|
||||
"code": "invalid_index_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn update_index_bad_primary_key() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("doggo");
|
||||
|
||||
let (response, code) = index.update_raw(json!({ "primaryKey": ["doggo"] })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value type at `.primaryKey`: expected a string, but found an array: `[\"doggo\"]`",
|
||||
"code": "invalid_index_primary_key",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_index_primary_key"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn update_index_immutable_uid() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("doggo");
|
||||
|
||||
let (response, code) = index.update_raw(json!({ "uid": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Immutable field `uid`: expected one of `primaryKey`",
|
||||
"code": "immutable_index_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#immutable_index_uid"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn update_index_immutable_created_at() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("doggo");
|
||||
|
||||
let (response, code) = index.update_raw(json!({ "createdAt": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Immutable field `createdAt`: expected one of `primaryKey`",
|
||||
"code": "immutable_index_created_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#immutable_index_created_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn update_index_immutable_updated_at() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("doggo");
|
||||
|
||||
let (response, code) = index.update_raw(json!({ "updatedAt": "doggo" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Immutable field `updatedAt`: expected one of `primaryKey`",
|
||||
"code": "immutable_index_updated_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#immutable_index_updated_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn update_index_unknown_field() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("doggo");
|
||||
|
||||
let (response, code) = index.update_raw(json!({ "doggo": "bork" })).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Unknown field `doggo`: expected one of `primaryKey`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn update_index_bad_uid() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("the good doggo");
|
||||
|
||||
let (response, code) = index.update_raw(json!({ "primaryKey": "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 (_).",
|
||||
"code": "invalid_index_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn delete_index_bad_uid() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("the good doggo");
|
||||
|
||||
let (response, code) = index.delete().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 (_).",
|
||||
"code": "invalid_index_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"
|
||||
}
|
||||
"###);
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
use meili_snap::{json_string, snapshot};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::common::Server;
|
||||
|
@ -34,7 +35,7 @@ async fn error_get_unexisting_index() {
|
|||
"message": "Index `test` not found.",
|
||||
"code": "index_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#index_not_found"
|
||||
});
|
||||
|
||||
assert_eq!(response, expected_response);
|
||||
|
@ -182,15 +183,13 @@ async fn get_invalid_index_uid() {
|
|||
let index = server.index("this is not a valid index name");
|
||||
let (response, code) = index.get().await;
|
||||
|
||||
assert_eq!(code, 404);
|
||||
assert_eq!(
|
||||
response,
|
||||
json!(
|
||||
{
|
||||
"message": "Index `this is not a valid index name` not found.",
|
||||
"code": "index_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-not-found"
|
||||
})
|
||||
);
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`this is not a valid index name` 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"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
mod create_index;
|
||||
mod delete_index;
|
||||
mod errors;
|
||||
mod get_index;
|
||||
mod stats;
|
||||
mod update_index;
|
||||
|
|
|
@ -55,7 +55,7 @@ async fn error_get_stats_unexisting_index() {
|
|||
"message": "Index `test` not found.",
|
||||
"code": "index_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#index_not_found"
|
||||
});
|
||||
|
||||
assert_eq!(response, expected_response);
|
||||
|
|
|
@ -98,7 +98,7 @@ async fn error_update_existing_primary_key() {
|
|||
"message": "Index already has a primary key: `id`.",
|
||||
"code": "index_primary_key_already_exists",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-primary-key-already-exists"
|
||||
"link": "https://docs.meilisearch.com/errors#index_primary_key_already_exists"
|
||||
});
|
||||
|
||||
assert_eq!(response["error"], expected_response);
|
||||
|
@ -117,7 +117,7 @@ async fn error_update_unexisting_index() {
|
|||
"message": "Index `test` not found.",
|
||||
"code": "index_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#index_not_found"
|
||||
});
|
||||
|
||||
assert_eq!(response["error"], expected_response);
|
||||
|
|
|
@ -8,6 +8,7 @@ mod search;
|
|||
mod settings;
|
||||
mod snapshot;
|
||||
mod stats;
|
||||
mod swap_indexes;
|
||||
mod tasks;
|
||||
|
||||
// Tests are isolated by features in different modules to allow better readability, test
|
||||
|
|
|
@ -13,7 +13,7 @@ async fn search_unexisting_index() {
|
|||
"message": "Index `test` not found.",
|
||||
"code": "index_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#index_not_found"
|
||||
});
|
||||
|
||||
index
|
||||
|
@ -46,10 +46,10 @@ async fn search_bad_q() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at `.q`.",
|
||||
"message": "Invalid value type at `.q`: expected a string, but found an array: `[\"doggo\"]`",
|
||||
"code": "invalid_search_q",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-q"
|
||||
"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.
|
||||
|
@ -64,21 +64,21 @@ async fn search_bad_offset() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Integer at `.offset`.",
|
||||
"message": "Invalid value type at `.offset`: expected a positive integer, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_search_offset",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-offset"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_offset"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.search_get(json!({"offset": "doggo"})).await;
|
||||
let (response, code) = index.search_get("offset=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.offset`.",
|
||||
"message": "Invalid value in parameter `offset`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_search_offset",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-offset"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_offset"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -92,21 +92,21 @@ async fn search_bad_limit() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Integer at `.limit`.",
|
||||
"message": "Invalid value type at `.limit`: expected a positive integer, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_search_limit",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-limit"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_limit"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.search_get(json!({"limit": "doggo"})).await;
|
||||
let (response, code) = index.search_get("limit=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.limit`.",
|
||||
"message": "Invalid value in parameter `limit`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_search_limit",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-limit"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_limit"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -120,21 +120,21 @@ async fn search_bad_page() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Integer at `.page`.",
|
||||
"message": "Invalid value type at `.page`: expected a positive integer, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_search_page",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-page"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_page"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.search_get(json!({"page": "doggo"})).await;
|
||||
let (response, code) = index.search_get("page=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.page`.",
|
||||
"message": "Invalid value in parameter `page`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_search_page",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-page"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_page"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -148,21 +148,21 @@ async fn search_bad_hits_per_page() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Integer at `.hitsPerPage`.",
|
||||
"message": "Invalid value type at `.hitsPerPage`: expected a positive integer, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_search_hits_per_page",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-hits-per-page"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_hits_per_page"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.search_get(json!({"hitsPerPage": "doggo"})).await;
|
||||
let (response, code) = index.search_get("hitsPerPage=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.hitsPerPage`.",
|
||||
"message": "Invalid value in parameter `hitsPerPage`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_search_hits_per_page",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-hits-per-page"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_hits_per_page"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -176,10 +176,10 @@ async fn search_bad_attributes_to_crop() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.attributesToCrop`.",
|
||||
"message": "Invalid value type at `.attributesToCrop`: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_search_attributes_to_crop",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-attributes-to-crop"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_attributes_to_crop"
|
||||
}
|
||||
"###);
|
||||
// Can't make the `attributes_to_crop` fail with a get search since it'll accept anything as an array of strings.
|
||||
|
@ -194,21 +194,21 @@ async fn search_bad_crop_length() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Integer at `.cropLength`.",
|
||||
"message": "Invalid value type at `.cropLength`: expected a positive integer, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_search_crop_length",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-crop-length"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_crop_length"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.search_get(json!({"cropLength": "doggo"})).await;
|
||||
let (response, code) = index.search_get("cropLength=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.cropLength`.",
|
||||
"message": "Invalid value in parameter `cropLength`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_search_crop_length",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-crop-length"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_crop_length"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -222,10 +222,10 @@ async fn search_bad_attributes_to_highlight() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.attributesToHighlight`.",
|
||||
"message": "Invalid value type at `.attributesToHighlight`: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_search_attributes_to_highlight",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-attributes-to-highlight"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_attributes_to_highlight"
|
||||
}
|
||||
"###);
|
||||
// Can't make the `attributes_to_highlight` fail with a get search since it'll accept anything as an array of strings.
|
||||
|
@ -251,7 +251,7 @@ async fn search_bad_filter() {
|
|||
"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"
|
||||
"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.
|
||||
|
@ -266,10 +266,10 @@ async fn search_bad_sort() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.sort`.",
|
||||
"message": "Invalid value type at `.sort`: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_search_sort",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-sort"
|
||||
"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.
|
||||
|
@ -284,21 +284,21 @@ async fn search_bad_show_matches_position() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Boolean at `.showMatchesPosition`.",
|
||||
"message": "Invalid value type at `.showMatchesPosition`: expected a boolean, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_search_show_matches_position",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-show-matches-position"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_show_matches_position"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.search_get(json!({"showMatchesPosition": "doggo"})).await;
|
||||
let (response, code) = index.search_get("showMatchesPosition=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "provided string was not `true` or `false` at `.showMatchesPosition`.",
|
||||
"message": "Invalid value in parameter `showMatchesPosition`: could not parse `doggo` as a boolean, expected either `true` or `false`",
|
||||
"code": "invalid_search_show_matches_position",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-show-matches-position"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_show_matches_position"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -312,15 +312,136 @@ async fn search_bad_facets() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.facets`.",
|
||||
"message": "Invalid value type at `.facets`: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-facets"
|
||||
"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_non_filterable_facets() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
index.update_settings(json!({"filterableAttributes": ["title"]})).await;
|
||||
// Wait for the settings update to complete
|
||||
index.wait_task(0).await;
|
||||
|
||||
let (response, code) = index.search_post(json!({"facets": ["doggo"]})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid facet distribution, attribute `doggo` is not filterable. The available filterable attribute is `title`.",
|
||||
"code": "invalid_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_facets"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.search_get("facets=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid facet distribution, attribute `doggo` is not filterable. The available filterable attribute is `title`.",
|
||||
"code": "invalid_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_facets"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_non_filterable_facets_multiple_filterable() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
index.update_settings(json!({"filterableAttributes": ["title", "genres"]})).await;
|
||||
index.wait_task(0).await;
|
||||
|
||||
let (response, code) = index.search_post(json!({"facets": ["doggo"]})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid facet distribution, attribute `doggo` is not filterable. The available filterable attributes are `genres, title`.",
|
||||
"code": "invalid_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_facets"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.search_get("facets=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid facet distribution, attribute `doggo` is not filterable. The available filterable attributes are `genres, title`.",
|
||||
"code": "invalid_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_facets"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_non_filterable_facets_no_filterable() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
index.update_settings(json!({"filterableAttributes": []})).await;
|
||||
index.wait_task(0).await;
|
||||
|
||||
let (response, code) = index.search_post(json!({"facets": ["doggo"]})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid facet distribution, this index does not have configured filterable attributes.",
|
||||
"code": "invalid_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_facets"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.search_get("facets=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid facet distribution, this index does not have configured filterable attributes.",
|
||||
"code": "invalid_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_facets"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_non_filterable_facets_multiple_facets() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
index.update_settings(json!({"filterableAttributes": ["title", "genres"]})).await;
|
||||
index.wait_task(0).await;
|
||||
|
||||
let (response, code) = index.search_post(json!({"facets": ["doggo", "neko"]})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid facet distribution, attributes `doggo, neko` are not filterable. The available filterable attributes are `genres, title`.",
|
||||
"code": "invalid_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_facets"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.search_get("facets=doggo,neko").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid facet distribution, attributes `doggo, neko` are not filterable. The available filterable attributes are `genres, title`.",
|
||||
"code": "invalid_search_facets",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_facets"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn search_bad_highlight_pre_tag() {
|
||||
let server = Server::new().await;
|
||||
|
@ -330,10 +451,10 @@ async fn search_bad_highlight_pre_tag() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at `.highlightPreTag`.",
|
||||
"message": "Invalid value type at `.highlightPreTag`: expected a string, but found an array: `[\"doggo\"]`",
|
||||
"code": "invalid_search_highlight_pre_tag",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-highlight-pre-tag"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_highlight_pre_tag"
|
||||
}
|
||||
"###);
|
||||
// Can't make the `highlight_pre_tag` fail with a get search since it'll accept anything as a strings.
|
||||
|
@ -348,10 +469,10 @@ async fn search_bad_highlight_post_tag() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at `.highlightPostTag`.",
|
||||
"message": "Invalid value type at `.highlightPostTag`: expected a string, but found an array: `[\"doggo\"]`",
|
||||
"code": "invalid_search_highlight_post_tag",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-highlight-post-tag"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_highlight_post_tag"
|
||||
}
|
||||
"###);
|
||||
// Can't make the `highlight_post_tag` fail with a get search since it'll accept anything as a strings.
|
||||
|
@ -366,10 +487,10 @@ async fn search_bad_crop_marker() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at `.cropMarker`.",
|
||||
"message": "Invalid value type at `.cropMarker`: expected a string, but found an array: `[\"doggo\"]`",
|
||||
"code": "invalid_search_crop_marker",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-crop-marker"
|
||||
"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.
|
||||
|
@ -384,21 +505,32 @@ async fn search_bad_matching_strategy() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: unknown value `doggo`, expected one of `last`, `all` at `.matchingStrategy`.",
|
||||
"message": "Unknown value `doggo` at `.matchingStrategy`: expected one of `last`, `all`",
|
||||
"code": "invalid_search_matching_strategy",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-matching-strategy"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_matching_strategy"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.search_get(json!({"matchingStrategy": "doggo"})).await;
|
||||
let (response, code) = index.search_post(json!({"matchingStrategy": {"doggo": "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`.",
|
||||
"message": "Invalid value type at `.matchingStrategy`: expected a string, but found an object: `{\"doggo\":\"doggo\"}`",
|
||||
"code": "invalid_search_matching_strategy",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-matching-strategy"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_matching_strategy"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index.search_get("matchingStrategy=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Unknown value `doggo` for parameter `matchingStrategy`: expected one of `last`, `all`",
|
||||
"code": "invalid_search_matching_strategy",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_matching_strategy"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -418,7 +550,7 @@ async fn filter_invalid_syntax_object() {
|
|||
"message": "Was expecting an operation `=`, `!=`, `>=`, `>`, `<=`, `<`, `IN`, `NOT IN`, `TO`, `EXISTS`, `NOT EXISTS`, `_geoRadius`, or `_geoBoundingBox` at `title & Glass`.\n1:14 title & Glass",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": "title & Glass"}), |response, code| {
|
||||
|
@ -443,7 +575,7 @@ async fn filter_invalid_syntax_array() {
|
|||
"message": "Was expecting an operation `=`, `!=`, `>=`, `>`, `<=`, `<`, `IN`, `NOT IN`, `TO`, `EXISTS`, `NOT EXISTS`, `_geoRadius`, or `_geoBoundingBox` at `title & Glass`.\n1:14 title & Glass",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": ["title & Glass"]}), |response, code| {
|
||||
|
@ -468,7 +600,7 @@ async fn filter_invalid_syntax_string() {
|
|||
"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_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": "title = Glass XOR title = Glass"}), |response, code| {
|
||||
|
@ -493,7 +625,7 @@ async fn filter_invalid_attribute_array() {
|
|||
"message": "Attribute `many` is not filterable. Available filterable attributes are: `title`.\n1:5 many = Glass",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": ["many = Glass"]}), |response, code| {
|
||||
|
@ -518,7 +650,7 @@ async fn filter_invalid_attribute_string() {
|
|||
"message": "Attribute `many` is not filterable. Available filterable attributes are: `title`.\n1:5 many = Glass",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": "many = Glass"}), |response, code| {
|
||||
|
@ -543,7 +675,7 @@ async fn filter_reserved_geo_attribute_array() {
|
|||
"message": "`_geo` is a reserved keyword and thus can't be used as a filter expression. Use the `_geoRadius(latitude, longitude, distance)` or `_geoBoundingBox([latitude, longitude], [latitude, longitude])` built-in rules to filter on `_geo` field coordinates.\n1:5 _geo = Glass",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": ["_geo = Glass"]}), |response, code| {
|
||||
|
@ -568,7 +700,7 @@ async fn filter_reserved_geo_attribute_string() {
|
|||
"message": "`_geo` is a reserved keyword and thus can't be used as a filter expression. Use the `_geoRadius(latitude, longitude, distance)` or `_geoBoundingBox([latitude, longitude], [latitude, longitude])` built-in rules to filter on `_geo` field coordinates.\n1:5 _geo = Glass",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": "_geo = Glass"}), |response, code| {
|
||||
|
@ -593,7 +725,7 @@ async fn filter_reserved_attribute_array() {
|
|||
"message": "`_geoDistance` is a reserved keyword and thus can't be used as a filter expression.\n1:13 _geoDistance = Glass",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": ["_geoDistance = Glass"]}), |response, code| {
|
||||
|
@ -618,7 +750,7 @@ async fn filter_reserved_attribute_string() {
|
|||
"message": "`_geoDistance` is a reserved keyword and thus can't be used as a filter expression.\n1:13 _geoDistance = Glass",
|
||||
"code": "invalid_search_filter",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-filter"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"
|
||||
});
|
||||
index
|
||||
.search(json!({"filter": "_geoDistance = Glass"}), |response, code| {
|
||||
|
@ -643,7 +775,7 @@ async fn sort_geo_reserved_attribute() {
|
|||
"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_search_sort",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-sort"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_sort"
|
||||
});
|
||||
index
|
||||
.search(
|
||||
|
@ -673,7 +805,7 @@ async fn sort_reserved_attribute() {
|
|||
"message": "`_geoDistance` is a reserved keyword and thus can't be used as a sort expression.",
|
||||
"code": "invalid_search_sort",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-sort"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_sort"
|
||||
});
|
||||
index
|
||||
.search(
|
||||
|
@ -703,7 +835,7 @@ async fn sort_unsortable_attribute() {
|
|||
"message": "Attribute `title` is not sortable. Available sortable attributes are: `id`.",
|
||||
"code": "invalid_search_sort",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-sort"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_sort"
|
||||
});
|
||||
index
|
||||
.search(
|
||||
|
@ -733,7 +865,7 @@ async fn sort_invalid_syntax() {
|
|||
"message": "Invalid syntax for the sort parameter: expected expression ending by `:asc` or `:desc`, found `title`.",
|
||||
"code": "invalid_search_sort",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-sort"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_sort"
|
||||
});
|
||||
index
|
||||
.search(
|
||||
|
@ -767,7 +899,7 @@ async fn sort_unset_ranking_rule() {
|
|||
"message": "The sort ranking rule must be specified in the ranking rules settings to use the sort parameter at search time.",
|
||||
"code": "invalid_search_sort",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-search-sort"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_search_sort"
|
||||
});
|
||||
index
|
||||
.search(
|
||||
|
|
|
@ -12,10 +12,10 @@ async fn settings_bad_displayed_attributes() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.displayedAttributes`.",
|
||||
"message": "Invalid value type at `.displayedAttributes`: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_displayed_attributes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-displayed-attributes"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_displayed_attributes"
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -23,10 +23,10 @@ async fn settings_bad_displayed_attributes() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.",
|
||||
"message": "Invalid value type: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_displayed_attributes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-displayed-attributes"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_displayed_attributes"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -40,10 +40,10 @@ async fn settings_bad_searchable_attributes() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.searchableAttributes`.",
|
||||
"message": "Invalid value type at `.searchableAttributes`: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_searchable_attributes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-searchable-attributes"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_searchable_attributes"
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -51,10 +51,10 @@ async fn settings_bad_searchable_attributes() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.",
|
||||
"message": "Invalid value type: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_searchable_attributes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-searchable-attributes"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_searchable_attributes"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -68,10 +68,10 @@ async fn settings_bad_filterable_attributes() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.filterableAttributes`.",
|
||||
"message": "Invalid value type at `.filterableAttributes`: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_filterable_attributes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-filterable-attributes"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_filterable_attributes"
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -79,10 +79,10 @@ async fn settings_bad_filterable_attributes() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.",
|
||||
"message": "Invalid value type: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_filterable_attributes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-filterable-attributes"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_filterable_attributes"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -96,10 +96,10 @@ async fn settings_bad_sortable_attributes() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.sortableAttributes`.",
|
||||
"message": "Invalid value type at `.sortableAttributes`: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_sortable_attributes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-sortable-attributes"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_sortable_attributes"
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -107,10 +107,10 @@ async fn settings_bad_sortable_attributes() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.",
|
||||
"message": "Invalid value type: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_sortable_attributes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-sortable-attributes"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_sortable_attributes"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -124,10 +124,10 @@ async fn settings_bad_ranking_rules() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.rankingRules`.",
|
||||
"message": "Invalid value type at `.rankingRules`: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_ranking_rules",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-ranking-rules"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_ranking_rules"
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -135,10 +135,10 @@ async fn settings_bad_ranking_rules() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.",
|
||||
"message": "Invalid value type: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_ranking_rules",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-ranking-rules"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_ranking_rules"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -152,10 +152,10 @@ async fn settings_bad_stop_words() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at `.stopWords`.",
|
||||
"message": "Invalid value type at `.stopWords`: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_stop_words",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-stop-words"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_stop_words"
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -163,10 +163,10 @@ async fn settings_bad_stop_words() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Sequence at ``.",
|
||||
"message": "Invalid value type: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_stop_words",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-stop-words"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_stop_words"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -180,10 +180,10 @@ async fn settings_bad_synonyms() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Map at `.synonyms`.",
|
||||
"message": "Invalid value type at `.synonyms`: expected an object, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_synonyms",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-synonyms"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_synonyms"
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -191,10 +191,10 @@ async fn settings_bad_synonyms() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Map at ``.",
|
||||
"message": "Invalid value type: expected an object, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_synonyms",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-synonyms"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_synonyms"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -208,10 +208,10 @@ async fn settings_bad_distinct_attribute() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at `.distinctAttribute`.",
|
||||
"message": "Invalid value type at `.distinctAttribute`: expected a string, but found an array: `[\"doggo\"]`",
|
||||
"code": "invalid_settings_distinct_attribute",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-distinct-attribute"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_distinct_attribute"
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -219,10 +219,10 @@ async fn settings_bad_distinct_attribute() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: Sequence `[\"doggo\"]`, expected a String at ``.",
|
||||
"message": "Invalid value type: expected a string, but found an array: `[\"doggo\"]`",
|
||||
"code": "invalid_settings_distinct_attribute",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-distinct-attribute"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_distinct_attribute"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -236,10 +236,22 @@ async fn settings_bad_typo_tolerance() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Map at `.typoTolerance`.",
|
||||
"message": "Invalid value type at `.typoTolerance`: expected an object, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_typo_tolerance",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-typo-tolerance"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_typo_tolerance"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) =
|
||||
index.update_settings(json!({ "typoTolerance": { "minWordSizeForTypos": "doggo" }})).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value type at `.typoTolerance.minWordSizeForTypos`: expected an object, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_typo_tolerance",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_typo_tolerance"
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -247,10 +259,25 @@ async fn settings_bad_typo_tolerance() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Map at ``.",
|
||||
"message": "Invalid value type: expected an object, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_typo_tolerance",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-typo-tolerance"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_typo_tolerance"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = index
|
||||
.update_settings_typo_tolerance(
|
||||
json!({ "typoTolerance": { "minWordSizeForTypos": "doggo" }}),
|
||||
)
|
||||
.await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Unknown field `typoTolerance`: expected one of `enabled`, `minWordSizeForTypos`, `disableOnWords`, `disableOnAttributes`",
|
||||
"code": "invalid_settings_typo_tolerance",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_typo_tolerance"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -264,10 +291,10 @@ async fn settings_bad_faceting() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Map at `.faceting`.",
|
||||
"message": "Invalid value type at `.faceting`: expected an object, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_faceting",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-faceting"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_faceting"
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -275,10 +302,10 @@ async fn settings_bad_faceting() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Map at ``.",
|
||||
"message": "Invalid value type: expected an object, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_faceting",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-faceting"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_faceting"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -292,10 +319,10 @@ async fn settings_bad_pagination() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Map at `.pagination`.",
|
||||
"message": "Invalid value type at `.pagination`: expected an object, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_pagination",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-pagination"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_pagination"
|
||||
}
|
||||
"###);
|
||||
|
||||
|
@ -303,10 +330,10 @@ async fn settings_bad_pagination() {
|
|||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid type: String `\"doggo\"`, expected a Map at ``.",
|
||||
"message": "Invalid value type: expected an object, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_settings_pagination",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-pagination"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_pagination"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
|
|
@ -185,7 +185,7 @@ async fn error_update_setting_unexisting_index_invalid_uid() {
|
|||
"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"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -282,10 +282,10 @@ async fn error_set_invalid_ranking_rules() {
|
|||
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]`.",
|
||||
"message": "Invalid value at `.rankingRules[0]`: `manyTheFish` ranking rule is invalid. Valid ranking rules are words, typo, sort, proximity, attribute, exactness and custom ranking rules.",
|
||||
"code": "invalid_settings_ranking_rules",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-ranking-rules"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_settings_ranking_rules"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
|
94
meilisearch/tests/swap_indexes/errors.rs
Normal file
94
meilisearch/tests/swap_indexes/errors.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
use meili_snap::*;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::Server;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn swap_indexes_bad_format() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.index_swap(json!("doggo")).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value type: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.index_swap(json!(["doggo"])).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value type at `[0]`: expected an object, but found a string: `\"doggo\"`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn swap_indexes_bad_indexes() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.index_swap(json!([{ "indexes": "doggo"}])).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value type at `[0].indexes`: expected an array, but found a string: `\"doggo\"`",
|
||||
"code": "invalid_swap_indexes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_swap_indexes"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.index_swap(json!([{ "indexes": ["doggo"]}])).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Two indexes must be given for each swap. The list `[\"doggo\"]` contains 1 indexes.",
|
||||
"code": "invalid_swap_indexes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_swap_indexes"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) =
|
||||
server.index_swap(json!([{ "indexes": ["doggo", "crabo", "croco"]}])).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Two indexes must be given for each swap. The list `[\"doggo\", \"crabo\", \"croco\"]` contains 3 indexes.",
|
||||
"code": "invalid_swap_indexes",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_swap_indexes"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.index_swap(json!([{ "indexes": ["doggo", "doggo"]}])).await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Indexes must be declared only once during a swap. `doggo` was specified several times.",
|
||||
"code": "invalid_swap_duplicate_index_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_swap_duplicate_index_found"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server
|
||||
.index_swap(json!([{ "indexes": ["doggo", "catto"]}, { "indexes": ["girafo", "doggo"]}]))
|
||||
.await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Indexes must be declared only once during a swap. `doggo` was specified several times.",
|
||||
"code": "invalid_swap_duplicate_index_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_swap_duplicate_index_found"
|
||||
}
|
||||
"###);
|
||||
}
|
357
meilisearch/tests/swap_indexes/mod.rs
Normal file
357
meilisearch/tests/swap_indexes/mod.rs
Normal file
|
@ -0,0 +1,357 @@
|
|||
mod errors;
|
||||
|
||||
use meili_snap::{json_string, snapshot};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::{GetAllDocumentsOptions, Server};
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn swap_indexes() {
|
||||
let server = Server::new().await;
|
||||
let a = server.index("a");
|
||||
let (_, code) = a.add_documents(json!({ "id": 1, "index": "a"}), None).await;
|
||||
snapshot!(code, @"202 Accepted");
|
||||
let b = server.index("b");
|
||||
let (res, code) = b.add_documents(json!({ "id": 1, "index": "b"}), None).await;
|
||||
snapshot!(code, @"202 Accepted");
|
||||
snapshot!(res["taskUid"], @"1");
|
||||
server.wait_task(1).await;
|
||||
|
||||
let (tasks, code) = server.tasks().await;
|
||||
snapshot!(code, @"200 OK");
|
||||
snapshot!(json_string!(tasks, { ".results[].duration" => "[duration]", ".results[].enqueuedAt" => "[date]", ".results[].startedAt" => "[date]", ".results[].finishedAt" => "[date]" }), @r###"
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uid": 1,
|
||||
"indexUid": "b",
|
||||
"status": "succeeded",
|
||||
"type": "documentAdditionOrUpdate",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"receivedDocuments": 1,
|
||||
"indexedDocuments": 1
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
},
|
||||
{
|
||||
"uid": 0,
|
||||
"indexUid": "a",
|
||||
"status": "succeeded",
|
||||
"type": "documentAdditionOrUpdate",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"receivedDocuments": 1,
|
||||
"indexedDocuments": 1
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
}
|
||||
],
|
||||
"limit": 20,
|
||||
"from": 1,
|
||||
"next": null
|
||||
}
|
||||
"###);
|
||||
|
||||
let (res, code) = server.index_swap(json!([{ "indexes": ["a", "b"] }])).await;
|
||||
snapshot!(code, @"202 Accepted");
|
||||
snapshot!(res["taskUid"], @"2");
|
||||
server.wait_task(2).await;
|
||||
|
||||
let (tasks, code) = server.tasks().await;
|
||||
snapshot!(code, @"200 OK");
|
||||
|
||||
// Notice how the task 0 which was initially representing the creation of the index `A` now represents the creation of the index `B`.
|
||||
snapshot!(json_string!(tasks, { ".results[].duration" => "[duration]", ".results[].enqueuedAt" => "[date]", ".results[].startedAt" => "[date]", ".results[].finishedAt" => "[date]" }), @r###"
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uid": 2,
|
||||
"indexUid": null,
|
||||
"status": "succeeded",
|
||||
"type": "indexSwap",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"swaps": [
|
||||
{
|
||||
"indexes": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
},
|
||||
{
|
||||
"uid": 1,
|
||||
"indexUid": "a",
|
||||
"status": "succeeded",
|
||||
"type": "documentAdditionOrUpdate",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"receivedDocuments": 1,
|
||||
"indexedDocuments": 1
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
},
|
||||
{
|
||||
"uid": 0,
|
||||
"indexUid": "b",
|
||||
"status": "succeeded",
|
||||
"type": "documentAdditionOrUpdate",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"receivedDocuments": 1,
|
||||
"indexedDocuments": 1
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
}
|
||||
],
|
||||
"limit": 20,
|
||||
"from": 2,
|
||||
"next": null
|
||||
}
|
||||
"###);
|
||||
|
||||
// BUT, the data in `a` should now points to the data that was in `b`.
|
||||
// And the opposite is true as well
|
||||
let (res, _) = a.get_all_documents(GetAllDocumentsOptions::default()).await;
|
||||
snapshot!(res["results"], @r###"[{"id":1,"index":"b"}]"###);
|
||||
let (res, _) = b.get_all_documents(GetAllDocumentsOptions::default()).await;
|
||||
snapshot!(res["results"], @r###"[{"id":1,"index":"a"}]"###);
|
||||
|
||||
// ================
|
||||
// And now we're going to attempt the famous and dangerous DOUBLE index swap 🤞
|
||||
|
||||
let c = server.index("c");
|
||||
let (res, code) = c.add_documents(json!({ "id": 1, "index": "c"}), None).await;
|
||||
snapshot!(code, @"202 Accepted");
|
||||
snapshot!(res["taskUid"], @"3");
|
||||
let d = server.index("d");
|
||||
let (res, code) = d.add_documents(json!({ "id": 1, "index": "d"}), None).await;
|
||||
snapshot!(code, @"202 Accepted");
|
||||
snapshot!(res["taskUid"], @"4");
|
||||
server.wait_task(4).await;
|
||||
|
||||
// ensure the index creation worked properly
|
||||
let (tasks, code) = server.tasks_filter("limit=2").await;
|
||||
snapshot!(code, @"200 OK");
|
||||
snapshot!(json_string!(tasks, { ".results[].duration" => "[duration]", ".results[].enqueuedAt" => "[date]", ".results[].startedAt" => "[date]", ".results[].finishedAt" => "[date]" }), @r###"
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uid": 4,
|
||||
"indexUid": "d",
|
||||
"status": "succeeded",
|
||||
"type": "documentAdditionOrUpdate",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"receivedDocuments": 1,
|
||||
"indexedDocuments": 1
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
},
|
||||
{
|
||||
"uid": 3,
|
||||
"indexUid": "c",
|
||||
"status": "succeeded",
|
||||
"type": "documentAdditionOrUpdate",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"receivedDocuments": 1,
|
||||
"indexedDocuments": 1
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
}
|
||||
],
|
||||
"limit": 2,
|
||||
"from": 4,
|
||||
"next": 2
|
||||
}
|
||||
"###);
|
||||
|
||||
// It's happening 😲
|
||||
|
||||
let (res, code) =
|
||||
server.index_swap(json!([{ "indexes": ["a", "b"] }, { "indexes": ["c", "d"] } ])).await;
|
||||
snapshot!(res["taskUid"], @"5");
|
||||
snapshot!(code, @"202 Accepted");
|
||||
server.wait_task(5).await;
|
||||
|
||||
// ensure the index creation worked properly
|
||||
let (tasks, code) = server.tasks().await;
|
||||
snapshot!(code, @"200 OK");
|
||||
|
||||
// What should we check for each tasks in this test:
|
||||
// Task number;
|
||||
// 0. should have the indexUid `a` again
|
||||
// 1. should have the indexUid `b` again
|
||||
// 2. stays unchanged
|
||||
// 3. now have the indexUid `d` instead of `c`
|
||||
// 4. now have the indexUid `c` instead of `d`
|
||||
snapshot!(json_string!(tasks, { ".results[].duration" => "[duration]", ".results[].enqueuedAt" => "[date]", ".results[].startedAt" => "[date]", ".results[].finishedAt" => "[date]" }), @r###"
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"uid": 5,
|
||||
"indexUid": null,
|
||||
"status": "succeeded",
|
||||
"type": "indexSwap",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"swaps": [
|
||||
{
|
||||
"indexes": [
|
||||
"a",
|
||||
"b"
|
||||
]
|
||||
},
|
||||
{
|
||||
"indexes": [
|
||||
"c",
|
||||
"d"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
},
|
||||
{
|
||||
"uid": 4,
|
||||
"indexUid": "c",
|
||||
"status": "succeeded",
|
||||
"type": "documentAdditionOrUpdate",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"receivedDocuments": 1,
|
||||
"indexedDocuments": 1
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
},
|
||||
{
|
||||
"uid": 3,
|
||||
"indexUid": "d",
|
||||
"status": "succeeded",
|
||||
"type": "documentAdditionOrUpdate",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"receivedDocuments": 1,
|
||||
"indexedDocuments": 1
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
},
|
||||
{
|
||||
"uid": 2,
|
||||
"indexUid": null,
|
||||
"status": "succeeded",
|
||||
"type": "indexSwap",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"swaps": [
|
||||
{
|
||||
"indexes": [
|
||||
"b",
|
||||
"a"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
},
|
||||
{
|
||||
"uid": 1,
|
||||
"indexUid": "b",
|
||||
"status": "succeeded",
|
||||
"type": "documentAdditionOrUpdate",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"receivedDocuments": 1,
|
||||
"indexedDocuments": 1
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
},
|
||||
{
|
||||
"uid": 0,
|
||||
"indexUid": "a",
|
||||
"status": "succeeded",
|
||||
"type": "documentAdditionOrUpdate",
|
||||
"canceledBy": null,
|
||||
"details": {
|
||||
"receivedDocuments": 1,
|
||||
"indexedDocuments": 1
|
||||
},
|
||||
"error": null,
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
"startedAt": "[date]",
|
||||
"finishedAt": "[date]"
|
||||
}
|
||||
],
|
||||
"limit": 20,
|
||||
"from": 5,
|
||||
"next": null
|
||||
}
|
||||
"###);
|
||||
|
||||
// - The data in `a` should point to `a`.
|
||||
// - The data in `b` should point to `b`.
|
||||
// - The data in `c` should point to `d`.
|
||||
// - The data in `d` should point to `c`.
|
||||
let (res, _) = a.get_all_documents(GetAllDocumentsOptions::default()).await;
|
||||
snapshot!(res["results"], @r###"[{"id":1,"index":"a"}]"###);
|
||||
let (res, _) = b.get_all_documents(GetAllDocumentsOptions::default()).await;
|
||||
snapshot!(res["results"], @r###"[{"id":1,"index":"b"}]"###);
|
||||
let (res, _) = c.get_all_documents(GetAllDocumentsOptions::default()).await;
|
||||
snapshot!(res["results"], @r###"[{"id":1,"index":"d"}]"###);
|
||||
let (res, _) = d.get_all_documents(GetAllDocumentsOptions::default()).await;
|
||||
snapshot!(res["results"], @r###"[{"id":1,"index":"c"}]"###);
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
use meili_snap::*;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::common::Server;
|
||||
|
||||
|
@ -7,36 +6,47 @@ use crate::common::Server;
|
|||
async fn task_bad_uids() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"uids": "doggo"})).await;
|
||||
let (response, code) = server.tasks_filter("uids=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.uids`.",
|
||||
"message": "Invalid value in parameter `uids`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_task_uids",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-uids"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_uids"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"uids": "doggo"})).await;
|
||||
let (response, code) = server.cancel_tasks("uids=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.uids`.",
|
||||
"message": "Invalid value in parameter `uids`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_task_uids",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-uids"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_uids"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"uids": "doggo"})).await;
|
||||
let (response, code) = server.delete_tasks("uids=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.uids`.",
|
||||
"message": "Invalid value in parameter `uids`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_task_uids",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-uids"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_uids"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks("uids=1,dogo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Invalid value in parameter `uids[1]`: could not parse `dogo` as a positive integer",
|
||||
"code": "invalid_task_uids",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_uids"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -45,36 +55,36 @@ async fn task_bad_uids() {
|
|||
async fn task_bad_canceled_by() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"canceledBy": "doggo"})).await;
|
||||
let (response, code) = server.tasks_filter("canceledBy=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.canceledBy`.",
|
||||
"message": "Invalid value in parameter `canceledBy`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_task_canceled_by",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-canceled-by"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_canceled_by"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"canceledBy": "doggo"})).await;
|
||||
let (response, code) = server.cancel_tasks("canceledBy=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.canceledBy`.",
|
||||
"message": "Invalid value in parameter `canceledBy`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_task_canceled_by",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-canceled-by"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_canceled_by"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"canceledBy": "doggo"})).await;
|
||||
let (response, code) = server.delete_tasks("canceledBy=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.canceledBy`.",
|
||||
"message": "Invalid value in parameter `canceledBy`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_task_canceled_by",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-canceled-by"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_canceled_by"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -83,36 +93,36 @@ async fn task_bad_canceled_by() {
|
|||
async fn task_bad_types() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"types": "doggo"})).await;
|
||||
let (response, code) = server.tasks_filter("types=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is not a type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`. at `.types`.",
|
||||
"message": "Invalid value in parameter `types`: `doggo` is not a valid task type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`.",
|
||||
"code": "invalid_task_types",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-types"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_types"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"types": "doggo"})).await;
|
||||
let (response, code) = server.cancel_tasks("types=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is not a type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`. at `.types`.",
|
||||
"message": "Invalid value in parameter `types`: `doggo` is not a valid task type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`.",
|
||||
"code": "invalid_task_types",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-types"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_types"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"types": "doggo"})).await;
|
||||
let (response, code) = server.delete_tasks("types=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is not a type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`. at `.types`.",
|
||||
"message": "Invalid value in parameter `types`: `doggo` is not a valid task type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`.",
|
||||
"code": "invalid_task_types",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-types"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_types"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -121,36 +131,36 @@ async fn task_bad_types() {
|
|||
async fn task_bad_statuses() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"statuses": "doggo"})).await;
|
||||
let (response, code) = server.tasks_filter("statuses=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is not a status. Available status are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. at `.statuses`.",
|
||||
"message": "Invalid value in parameter `statuses`: `doggo` is not a valid task status. Available statuses are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`.",
|
||||
"code": "invalid_task_statuses",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-statuses"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_statuses"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"statuses": "doggo"})).await;
|
||||
let (response, code) = server.cancel_tasks("statuses=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is not a status. Available status are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. at `.statuses`.",
|
||||
"message": "Invalid value in parameter `statuses`: `doggo` is not a valid task status. Available statuses are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`.",
|
||||
"code": "invalid_task_statuses",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-statuses"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_statuses"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"statuses": "doggo"})).await;
|
||||
let (response, code) = server.delete_tasks("statuses=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is not a status. Available status are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. at `.statuses`.",
|
||||
"message": "Invalid value in parameter `statuses`: `doggo` is not a valid task status. Available statuses are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`.",
|
||||
"code": "invalid_task_statuses",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-statuses"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_statuses"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -159,36 +169,36 @@ async fn task_bad_statuses() {
|
|||
async fn task_bad_index_uids() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"indexUids": "the good doggo"})).await;
|
||||
let (response, code) = server.tasks_filter("indexUids=the%20good%20doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`the good doggo` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). at `.indexUids`.",
|
||||
"message": "Invalid value in parameter `indexUids`: `the good doggo` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).",
|
||||
"code": "invalid_index_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-index-uid"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"indexUids": "the good doggo"})).await;
|
||||
let (response, code) = server.cancel_tasks("indexUids=the%20good%20doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`the good doggo` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). at `.indexUids`.",
|
||||
"message": "Invalid value in parameter `indexUids`: `the good doggo` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).",
|
||||
"code": "invalid_index_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-index-uid"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"indexUids": "the good doggo"})).await;
|
||||
let (response, code) = server.delete_tasks("indexUids=the%20good%20doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`the good doggo` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). at `.indexUids`.",
|
||||
"message": "Invalid value in parameter `indexUids`: `the good doggo` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_).",
|
||||
"code": "invalid_index_uid",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-index-uid"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_index_uid"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -197,36 +207,36 @@ async fn task_bad_index_uids() {
|
|||
async fn task_bad_limit() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"limit": "doggo"})).await;
|
||||
let (response, code) = server.tasks_filter("limit=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.limit`.",
|
||||
"message": "Invalid value in parameter `limit`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_task_limit",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-limit"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_limit"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"limit": "doggo"})).await;
|
||||
let (response, code) = server.cancel_tasks("limit=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: unknown field `limit`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
|
||||
"message": "Unknown parameter `limit`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"limit": "doggo"})).await;
|
||||
let (response, code) = server.delete_tasks("limit=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: unknown field `limit`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
|
||||
"message": "Unknown parameter `limit`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -235,36 +245,36 @@ async fn task_bad_limit() {
|
|||
async fn task_bad_from() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"from": "doggo"})).await;
|
||||
let (response, code) = server.tasks_filter("from=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.from`.",
|
||||
"message": "Invalid value in parameter `from`: could not parse `doggo` as a positive integer",
|
||||
"code": "invalid_task_from",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-from"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_from"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"from": "doggo"})).await;
|
||||
let (response, code) = server.cancel_tasks("from=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: unknown field `from`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
|
||||
"message": "Unknown parameter `from`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"from": "doggo"})).await;
|
||||
let (response, code) = server.delete_tasks("from=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: unknown field `from`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
|
||||
"message": "Unknown parameter `from`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -273,36 +283,36 @@ async fn task_bad_from() {
|
|||
async fn task_bad_after_enqueued_at() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"afterEnqueuedAt": "doggo"})).await;
|
||||
let (response, code) = server.tasks_filter("afterEnqueuedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterEnqueuedAt`.",
|
||||
"message": "Invalid value in parameter `afterEnqueuedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_after_enqueued_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-enqueued-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_after_enqueued_at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"afterEnqueuedAt": "doggo"})).await;
|
||||
let (response, code) = server.cancel_tasks("afterEnqueuedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterEnqueuedAt`.",
|
||||
"message": "Invalid value in parameter `afterEnqueuedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_after_enqueued_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-enqueued-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_after_enqueued_at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"afterEnqueuedAt": "doggo"})).await;
|
||||
let (response, code) = server.delete_tasks("afterEnqueuedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterEnqueuedAt`.",
|
||||
"message": "Invalid value in parameter `afterEnqueuedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_after_enqueued_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-enqueued-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_after_enqueued_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -311,36 +321,36 @@ async fn task_bad_after_enqueued_at() {
|
|||
async fn task_bad_before_enqueued_at() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"beforeEnqueuedAt": "doggo"})).await;
|
||||
let (response, code) = server.tasks_filter("beforeEnqueuedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeEnqueuedAt`.",
|
||||
"message": "Invalid value in parameter `beforeEnqueuedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_before_enqueued_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-enqueued-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_before_enqueued_at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"beforeEnqueuedAt": "doggo"})).await;
|
||||
let (response, code) = server.cancel_tasks("beforeEnqueuedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeEnqueuedAt`.",
|
||||
"message": "Invalid value in parameter `beforeEnqueuedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_before_enqueued_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-enqueued-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_before_enqueued_at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"beforeEnqueuedAt": "doggo"})).await;
|
||||
let (response, code) = server.delete_tasks("beforeEnqueuedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeEnqueuedAt`.",
|
||||
"message": "Invalid value in parameter `beforeEnqueuedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_before_enqueued_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-enqueued-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_before_enqueued_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -349,36 +359,36 @@ async fn task_bad_before_enqueued_at() {
|
|||
async fn task_bad_after_started_at() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"afterStartedAt": "doggo"})).await;
|
||||
let (response, code) = server.tasks_filter("afterStartedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterStartedAt`.",
|
||||
"message": "Invalid value in parameter `afterStartedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_after_started_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-started-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_after_started_at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"afterStartedAt": "doggo"})).await;
|
||||
let (response, code) = server.cancel_tasks("afterStartedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterStartedAt`.",
|
||||
"message": "Invalid value in parameter `afterStartedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_after_started_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-started-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_after_started_at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"afterStartedAt": "doggo"})).await;
|
||||
let (response, code) = server.delete_tasks("afterStartedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterStartedAt`.",
|
||||
"message": "Invalid value in parameter `afterStartedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_after_started_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-started-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_after_started_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -387,36 +397,36 @@ async fn task_bad_after_started_at() {
|
|||
async fn task_bad_before_started_at() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"beforeStartedAt": "doggo"})).await;
|
||||
let (response, code) = server.tasks_filter("beforeStartedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeStartedAt`.",
|
||||
"message": "Invalid value in parameter `beforeStartedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_before_started_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-started-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_before_started_at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"beforeStartedAt": "doggo"})).await;
|
||||
let (response, code) = server.cancel_tasks("beforeStartedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeStartedAt`.",
|
||||
"message": "Invalid value in parameter `beforeStartedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_before_started_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-started-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_before_started_at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"beforeStartedAt": "doggo"})).await;
|
||||
let (response, code) = server.delete_tasks("beforeStartedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeStartedAt`.",
|
||||
"message": "Invalid value in parameter `beforeStartedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_before_started_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-started-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_before_started_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -425,36 +435,36 @@ async fn task_bad_before_started_at() {
|
|||
async fn task_bad_after_finished_at() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"afterFinishedAt": "doggo"})).await;
|
||||
let (response, code) = server.tasks_filter("afterFinishedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterFinishedAt`.",
|
||||
"message": "Invalid value in parameter `afterFinishedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_after_finished_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-finished-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_after_finished_at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"afterFinishedAt": "doggo"})).await;
|
||||
let (response, code) = server.cancel_tasks("afterFinishedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterFinishedAt`.",
|
||||
"message": "Invalid value in parameter `afterFinishedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_after_finished_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-finished-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_after_finished_at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"afterFinishedAt": "doggo"})).await;
|
||||
let (response, code) = server.delete_tasks("afterFinishedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterFinishedAt`.",
|
||||
"message": "Invalid value in parameter `afterFinishedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_after_finished_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-after-finished-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_after_finished_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -463,36 +473,36 @@ async fn task_bad_after_finished_at() {
|
|||
async fn task_bad_before_finished_at() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!({"beforeFinishedAt": "doggo"})).await;
|
||||
let (response, code) = server.tasks_filter("beforeFinishedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeFinishedAt`.",
|
||||
"message": "Invalid value in parameter `beforeFinishedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_before_finished_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-finished-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_before_finished_at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({"beforeFinishedAt": "doggo"})).await;
|
||||
let (response, code) = server.cancel_tasks("beforeFinishedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeFinishedAt`.",
|
||||
"message": "Invalid value in parameter `beforeFinishedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_before_finished_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-finished-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_before_finished_at"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({"beforeFinishedAt": "doggo"})).await;
|
||||
let (response, code) = server.delete_tasks("beforeFinishedAt=doggo").await;
|
||||
snapshot!(code, @"400 Bad Request");
|
||||
snapshot!(json_string!(response), @r###"
|
||||
{
|
||||
"message": "`doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeFinishedAt`.",
|
||||
"message": "Invalid value in parameter `beforeFinishedAt`: `doggo` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_before_finished_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-finished-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_before_finished_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ async fn error_get_unexisting_task_status() {
|
|||
"message": "Task `1` not found.",
|
||||
"code": "task_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#task-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#task_not_found"
|
||||
});
|
||||
|
||||
assert_eq!(response, expected_response);
|
||||
|
@ -115,7 +115,7 @@ async fn list_tasks_status_filtered() {
|
|||
.add_documents(serde_json::from_str(include_str!("../assets/test_set.json")).unwrap(), None)
|
||||
.await;
|
||||
|
||||
let (response, code) = index.filtered_tasks(&[], &["succeeded"]).await;
|
||||
let (response, code) = index.filtered_tasks(&[], &["succeeded"], &[]).await;
|
||||
assert_eq!(code, 200, "{}", response);
|
||||
assert_eq!(response["results"].as_array().unwrap().len(), 1);
|
||||
|
||||
|
@ -126,7 +126,7 @@ async fn list_tasks_status_filtered() {
|
|||
|
||||
index.wait_task(1).await;
|
||||
|
||||
let (response, code) = index.filtered_tasks(&[], &["succeeded"]).await;
|
||||
let (response, code) = index.filtered_tasks(&[], &["succeeded"], &[]).await;
|
||||
assert_eq!(code, 200, "{}", response);
|
||||
assert_eq!(response["results"].as_array().unwrap().len(), 2);
|
||||
}
|
||||
|
@ -141,16 +141,31 @@ async fn list_tasks_type_filtered() {
|
|||
.add_documents(serde_json::from_str(include_str!("../assets/test_set.json")).unwrap(), None)
|
||||
.await;
|
||||
|
||||
let (response, code) = index.filtered_tasks(&["indexCreation"], &[]).await;
|
||||
let (response, code) = index.filtered_tasks(&["indexCreation"], &[], &[]).await;
|
||||
assert_eq!(code, 200, "{}", response);
|
||||
assert_eq!(response["results"].as_array().unwrap().len(), 1);
|
||||
|
||||
let (response, code) =
|
||||
index.filtered_tasks(&["indexCreation", "documentAdditionOrUpdate"], &[]).await;
|
||||
index.filtered_tasks(&["indexCreation", "documentAdditionOrUpdate"], &[], &[]).await;
|
||||
assert_eq!(code, 200, "{}", response);
|
||||
assert_eq!(response["results"].as_array().unwrap().len(), 2);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn list_tasks_invalid_canceled_by_filter() {
|
||||
let server = Server::new().await;
|
||||
let index = server.index("test");
|
||||
index.create(None).await;
|
||||
index.wait_task(0).await;
|
||||
index
|
||||
.add_documents(serde_json::from_str(include_str!("../assets/test_set.json")).unwrap(), None)
|
||||
.await;
|
||||
|
||||
let (response, code) = index.filtered_tasks(&[], &[], &["0"]).await;
|
||||
assert_eq!(code, 200, "{}", response);
|
||||
assert_eq!(response["results"].as_array().unwrap().len(), 0);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn list_tasks_status_and_type_filtered() {
|
||||
let server = Server::new().await;
|
||||
|
@ -161,7 +176,7 @@ async fn list_tasks_status_and_type_filtered() {
|
|||
.add_documents(serde_json::from_str(include_str!("../assets/test_set.json")).unwrap(), None)
|
||||
.await;
|
||||
|
||||
let (response, code) = index.filtered_tasks(&["indexCreation"], &["failed"]).await;
|
||||
let (response, code) = index.filtered_tasks(&["indexCreation"], &["failed"], &[]).await;
|
||||
assert_eq!(code, 200, "{}", response);
|
||||
assert_eq!(response["results"].as_array().unwrap().len(), 0);
|
||||
|
||||
|
@ -169,6 +184,7 @@ async fn list_tasks_status_and_type_filtered() {
|
|||
.filtered_tasks(
|
||||
&["indexCreation", "documentAdditionOrUpdate"],
|
||||
&["succeeded", "processing", "enqueued"],
|
||||
&[],
|
||||
)
|
||||
.await;
|
||||
assert_eq!(code, 200, "{}", response);
|
||||
|
@ -179,47 +195,47 @@ async fn list_tasks_status_and_type_filtered() {
|
|||
async fn get_task_filter_error() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.tasks_filter(json!( { "lol": "pied" })).await;
|
||||
let (response, code) = server.tasks_filter("lol=pied").await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: unknown field `lol`, expected one of `limit`, `from`, `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
|
||||
"message": "Unknown parameter `lol`: expected one of `limit`, `from`, `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.tasks_filter(json!( { "uids": "pied" })).await;
|
||||
let (response, code) = server.tasks_filter("uids=pied").await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.uids`.",
|
||||
"message": "Invalid value in parameter `uids`: could not parse `pied` as a positive integer",
|
||||
"code": "invalid_task_uids",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-uids"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_uids"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.tasks_filter(json!( { "from": "pied" })).await;
|
||||
let (response, code) = server.tasks_filter("from=pied").await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.from`.",
|
||||
"message": "Invalid value in parameter `from`: could not parse `pied` as a positive integer",
|
||||
"code": "invalid_task_from",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-from"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_from"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.tasks_filter(json!( { "beforeStartedAt": "pied" })).await;
|
||||
let (response, code) = server.tasks_filter("beforeStartedAt=pied").await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "`pied` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeStartedAt`.",
|
||||
"message": "Invalid value in parameter `beforeStartedAt`: `pied` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format.",
|
||||
"code": "invalid_task_before_started_at",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-before-started-at"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_before_started_at"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -228,36 +244,36 @@ async fn get_task_filter_error() {
|
|||
async fn delete_task_filter_error() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.delete_tasks(json!(null)).await;
|
||||
let (response, code) = server.delete_tasks("").await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
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`.",
|
||||
"message": "Query parameters to filter the tasks to delete are missing. Available query parameters are: `uids`, `indexUids`, `statuses`, `types`, `canceledBy`, `beforeEnqueuedAt`, `afterEnqueuedAt`, `beforeStartedAt`, `afterStartedAt`, `beforeFinishedAt`, `afterFinishedAt`.",
|
||||
"code": "missing_task_filters",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#missing-task-filters"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_task_filters"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({ "lol": "pied" })).await;
|
||||
let (response, code) = server.delete_tasks("lol=pied").await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: unknown field `lol`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
|
||||
"message": "Unknown parameter `lol`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.delete_tasks(json!({ "uids": "pied" })).await;
|
||||
let (response, code) = server.delete_tasks("uids=pied").await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.uids`.",
|
||||
"message": "Invalid value in parameter `uids`: could not parse `pied` as a positive integer",
|
||||
"code": "invalid_task_uids",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-uids"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_uids"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -266,36 +282,36 @@ async fn delete_task_filter_error() {
|
|||
async fn cancel_task_filter_error() {
|
||||
let server = Server::new().await;
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!(null)).await;
|
||||
let (response, code) = server.cancel_tasks("").await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
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`.",
|
||||
"message": "Query parameters to filter the tasks to cancel are missing. Available query parameters are: `uids`, `indexUids`, `statuses`, `types`, `canceledBy`, `beforeEnqueuedAt`, `afterEnqueuedAt`, `beforeStartedAt`, `afterStartedAt`, `beforeFinishedAt`, `afterFinishedAt`.",
|
||||
"code": "missing_task_filters",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#missing-task-filters"
|
||||
"link": "https://docs.meilisearch.com/errors#missing_task_filters"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({ "lol": "pied" })).await;
|
||||
let (response, code) = server.cancel_tasks("lol=pied").await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "Json deserialize error: unknown field `lol`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``.",
|
||||
"message": "Unknown parameter `lol`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt`",
|
||||
"code": "bad_request",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#bad-request"
|
||||
"link": "https://docs.meilisearch.com/errors#bad_request"
|
||||
}
|
||||
"###);
|
||||
|
||||
let (response, code) = server.cancel_tasks(json!({ "uids": "pied" })).await;
|
||||
let (response, code) = server.cancel_tasks("uids=pied").await;
|
||||
assert_eq!(code, 400, "{}", response);
|
||||
meili_snap::snapshot!(meili_snap::json_string!(response), @r###"
|
||||
{
|
||||
"message": "invalid digit found in string at `.uids`.",
|
||||
"message": "Invalid value in parameter `uids`: could not parse `pied` as a positive integer",
|
||||
"code": "invalid_task_uids",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-task-uids"
|
||||
"link": "https://docs.meilisearch.com/errors#invalid_task_uids"
|
||||
}
|
||||
"###);
|
||||
}
|
||||
|
@ -350,7 +366,7 @@ async fn test_summarized_document_addition_or_update() {
|
|||
index.add_documents(json!({ "id": 42, "content": "doggos & fluff" }), None).await;
|
||||
index.wait_task(0).await;
|
||||
let (task, _) = index.get_task(0).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -374,7 +390,7 @@ async fn test_summarized_document_addition_or_update() {
|
|||
index.add_documents(json!({ "id": 42, "content": "doggos & fluff" }), Some("id")).await;
|
||||
index.wait_task(1).await;
|
||||
let (task, _) = index.get_task(1).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -403,7 +419,7 @@ async fn test_summarized_delete_batch() {
|
|||
index.delete_batch(vec![1, 2, 3]).await;
|
||||
index.wait_task(0).await;
|
||||
let (task, _) = index.get_task(0).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -420,7 +436,7 @@ async fn test_summarized_delete_batch() {
|
|||
"message": "Index `test` not found.",
|
||||
"code": "index_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#index_not_found"
|
||||
},
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
|
@ -433,7 +449,7 @@ async fn test_summarized_delete_batch() {
|
|||
index.delete_batch(vec![42]).await;
|
||||
index.wait_task(2).await;
|
||||
let (task, _) = index.get_task(2).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -462,7 +478,7 @@ async fn test_summarized_delete_document() {
|
|||
index.delete_document(1).await;
|
||||
index.wait_task(0).await;
|
||||
let (task, _) = index.get_task(0).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -479,7 +495,7 @@ async fn test_summarized_delete_document() {
|
|||
"message": "Index `test` not found.",
|
||||
"code": "index_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#index_not_found"
|
||||
},
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
|
@ -492,7 +508,7 @@ async fn test_summarized_delete_document() {
|
|||
index.delete_document(42).await;
|
||||
index.wait_task(2).await;
|
||||
let (task, _) = index.get_task(2).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -523,17 +539,17 @@ async fn test_summarized_settings_update() {
|
|||
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]`.",
|
||||
"message": "Invalid value at `.rankingRules[0]`: `custom` ranking rule is invalid. Valid ranking rules are words, typo, sort, proximity, attribute, exactness and custom ranking rules.",
|
||||
"code": "invalid_settings_ranking_rules",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-settings-ranking-rules"
|
||||
"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;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -571,7 +587,7 @@ async fn test_summarized_index_creation() {
|
|||
index.create(None).await;
|
||||
index.wait_task(0).await;
|
||||
let (task, _) = index.get_task(0).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -594,7 +610,7 @@ async fn test_summarized_index_creation() {
|
|||
index.create(Some("doggos")).await;
|
||||
index.wait_task(1).await;
|
||||
let (task, _) = index.get_task(1).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -610,7 +626,7 @@ async fn test_summarized_index_creation() {
|
|||
"message": "Index `test` already exists.",
|
||||
"code": "index_already_exists",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-already-exists"
|
||||
"link": "https://docs.meilisearch.com/errors#index_already_exists"
|
||||
},
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
|
@ -627,7 +643,7 @@ async fn test_summarized_index_deletion() {
|
|||
index.delete().await;
|
||||
index.wait_task(0).await;
|
||||
let (task, _) = index.get_task(0).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -643,7 +659,7 @@ async fn test_summarized_index_deletion() {
|
|||
"message": "Index `test` not found.",
|
||||
"code": "index_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#index_not_found"
|
||||
},
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
|
@ -657,7 +673,7 @@ async fn test_summarized_index_deletion() {
|
|||
index.delete().await;
|
||||
index.wait_task(2).await;
|
||||
let (task, _) = index.get_task(2).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -681,7 +697,7 @@ async fn test_summarized_index_deletion() {
|
|||
index.delete().await;
|
||||
index.wait_task(2).await;
|
||||
let (task, _) = index.get_task(2).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -710,7 +726,7 @@ async fn test_summarized_index_update() {
|
|||
index.update(None).await;
|
||||
index.wait_task(0).await;
|
||||
let (task, _) = index.get_task(0).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -726,7 +742,7 @@ async fn test_summarized_index_update() {
|
|||
"message": "Index `test` not found.",
|
||||
"code": "index_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#index_not_found"
|
||||
},
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
|
@ -738,7 +754,7 @@ async fn test_summarized_index_update() {
|
|||
index.update(Some("bones")).await;
|
||||
index.wait_task(1).await;
|
||||
let (task, _) = index.get_task(1).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -754,7 +770,7 @@ async fn test_summarized_index_update() {
|
|||
"message": "Index `test` not found.",
|
||||
"code": "index_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#index-not-found"
|
||||
"link": "https://docs.meilisearch.com/errors#index_not_found"
|
||||
},
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
|
@ -769,7 +785,7 @@ async fn test_summarized_index_update() {
|
|||
index.update(None).await;
|
||||
index.wait_task(3).await;
|
||||
let (task, _) = index.get_task(3).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -792,7 +808,7 @@ async fn test_summarized_index_update() {
|
|||
index.update(Some("bones")).await;
|
||||
index.wait_task(4).await;
|
||||
let (task, _) = index.get_task(4).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -823,7 +839,7 @@ async fn test_summarized_index_swap() {
|
|||
.await;
|
||||
server.wait_task(0).await;
|
||||
let (task, _) = server.get_task(0).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -844,9 +860,9 @@ async fn test_summarized_index_swap() {
|
|||
},
|
||||
"error": {
|
||||
"message": "Indexes `cattos`, `doggos` not found.",
|
||||
"code": "invalid_swap_indexes",
|
||||
"code": "index_not_found",
|
||||
"type": "invalid_request",
|
||||
"link": "https://docs.meilisearch.com/errors#invalid-swap-indexes"
|
||||
"link": "https://docs.meilisearch.com/errors#index_not_found"
|
||||
},
|
||||
"duration": "[duration]",
|
||||
"enqueuedAt": "[date]",
|
||||
|
@ -864,7 +880,7 @@ async fn test_summarized_index_swap() {
|
|||
.await;
|
||||
server.wait_task(3).await;
|
||||
let (task, _) = server.get_task(3).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -899,10 +915,10 @@ async fn test_summarized_task_cancelation() {
|
|||
// to avoid being flaky we're only going to cancel an already finished task :(
|
||||
index.create(None).await;
|
||||
index.wait_task(0).await;
|
||||
server.cancel_tasks(json!({ "uids": [0] })).await;
|
||||
server.cancel_tasks("uids=0").await;
|
||||
index.wait_task(1).await;
|
||||
let (task, _) = index.get_task(1).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -932,10 +948,10 @@ async fn test_summarized_task_deletion() {
|
|||
// to avoid being flaky we're only going to delete an already finished task :(
|
||||
index.create(None).await;
|
||||
index.wait_task(0).await;
|
||||
server.delete_tasks(json!({ "uids": [0] })).await;
|
||||
server.delete_tasks("uids=0").await;
|
||||
index.wait_task(1).await;
|
||||
let (task, _) = index.get_task(1).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
@ -964,7 +980,7 @@ async fn test_summarized_dump_creation() {
|
|||
server.create_dump().await;
|
||||
server.wait_task(0).await;
|
||||
let (task, _) = server.get_task(0).await;
|
||||
assert_json_snapshot!(task,
|
||||
assert_json_snapshot!(task,
|
||||
{ ".details.dumpUid" => "[dumpUid]", ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" },
|
||||
@r###"
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue