mirror of
https://github.com/meilisearch/MeiliSearch
synced 2024-11-29 16:24:26 +01:00
3370: make the swap indexes not found errors return an IndexNotFound error-code r=irevoire a=irevoire Fix https://github.com/meilisearch/meilisearch/issues/3368 3373: fix a wrong error code and add tests on the document resource r=irevoire a=irevoire Fix https://github.com/meilisearch/meilisearch/issues/3371 3375: Avoid deleting all task invalid canceled by r=irevoire a=Kerollmops Fixes #3369 by making sure that at least one `canceledBy` task filter parameter matches something. Co-authored-by: Tamo <tamo@meilisearch.com> Co-authored-by: Kerollmops <clement@meilisearch.com>
This commit is contained in:
commit
3e5b3df487
@ -141,8 +141,8 @@ impl ErrorCode for Error {
|
|||||||
Error::IndexAlreadyExists(_) => Code::IndexAlreadyExists,
|
Error::IndexAlreadyExists(_) => Code::IndexAlreadyExists,
|
||||||
Error::SwapDuplicateIndexesFound(_) => Code::InvalidSwapDuplicateIndexFound,
|
Error::SwapDuplicateIndexesFound(_) => Code::InvalidSwapDuplicateIndexFound,
|
||||||
Error::SwapDuplicateIndexFound(_) => Code::InvalidSwapDuplicateIndexFound,
|
Error::SwapDuplicateIndexFound(_) => Code::InvalidSwapDuplicateIndexFound,
|
||||||
Error::SwapIndexNotFound(_) => Code::InvalidSwapIndexes,
|
Error::SwapIndexNotFound(_) => Code::IndexNotFound,
|
||||||
Error::SwapIndexesNotFound(_) => Code::InvalidSwapIndexes,
|
Error::SwapIndexesNotFound(_) => Code::IndexNotFound,
|
||||||
Error::InvalidTaskDate { field, .. } => (*field).into(),
|
Error::InvalidTaskDate { field, .. } => (*field).into(),
|
||||||
Error::InvalidTaskUids { .. } => Code::InvalidTaskUids,
|
Error::InvalidTaskUids { .. } => Code::InvalidTaskUids,
|
||||||
Error::InvalidTaskStatuses { .. } => Code::InvalidTaskStatuses,
|
Error::InvalidTaskStatuses { .. } => Code::InvalidTaskStatuses,
|
||||||
|
@ -502,13 +502,22 @@ impl IndexScheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(canceled_by) = &query.canceled_by {
|
if let Some(canceled_by) = &query.canceled_by {
|
||||||
|
let mut all_canceled_tasks = RoaringBitmap::new();
|
||||||
for cancel_task_uid in canceled_by {
|
for cancel_task_uid in canceled_by {
|
||||||
if let Some(canceled_by_uid) =
|
if let Some(canceled_by_uid) =
|
||||||
self.canceled_by.get(rtxn, &BEU32::new(*cancel_task_uid))?
|
self.canceled_by.get(rtxn, &BEU32::new(*cancel_task_uid))?
|
||||||
{
|
{
|
||||||
tasks &= canceled_by_uid;
|
all_canceled_tasks |= canceled_by_uid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the canceled_by has been specified but no task
|
||||||
|
// matches then we prefer matching zero than all tasks.
|
||||||
|
if all_canceled_tasks.is_empty() {
|
||||||
|
return Ok(RoaringBitmap::new());
|
||||||
|
} else {
|
||||||
|
tasks &= all_canceled_tasks;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(kind) = &query.types {
|
if let Some(kind) = &query.types {
|
||||||
|
@ -10,7 +10,7 @@ source: index-scheduler/src/lib.rs
|
|||||||
1 {uid: 1, status: succeeded, details: { primary_key: Some("id") }, kind: IndexCreation { index_uid: "b", primary_key: Some("id") }}
|
1 {uid: 1, status: succeeded, details: { primary_key: Some("id") }, kind: IndexCreation { index_uid: "b", primary_key: Some("id") }}
|
||||||
2 {uid: 2, status: succeeded, details: { primary_key: Some("id") }, kind: IndexCreation { index_uid: "c", primary_key: Some("id") }}
|
2 {uid: 2, status: succeeded, details: { primary_key: Some("id") }, kind: IndexCreation { index_uid: "c", primary_key: Some("id") }}
|
||||||
3 {uid: 3, status: succeeded, details: { primary_key: Some("id") }, kind: IndexCreation { index_uid: "d", primary_key: Some("id") }}
|
3 {uid: 3, status: succeeded, details: { primary_key: Some("id") }, kind: IndexCreation { index_uid: "d", primary_key: Some("id") }}
|
||||||
4 {uid: 4, status: failed, error: ResponseError { code: 200, message: "Indexes `e`, `f` not found.", error_code: "invalid_swap_indexes", error_type: "invalid_request", error_link: "https://docs.meilisearch.com/errors#invalid-swap-indexes" }, details: { swaps: [IndexSwap { indexes: ("a", "b") }, IndexSwap { indexes: ("c", "e") }, IndexSwap { indexes: ("d", "f") }] }, kind: IndexSwap { swaps: [IndexSwap { indexes: ("a", "b") }, IndexSwap { indexes: ("c", "e") }, IndexSwap { indexes: ("d", "f") }] }}
|
4 {uid: 4, status: failed, error: ResponseError { code: 200, message: "Indexes `e`, `f` not found.", error_code: "index_not_found", error_type: "invalid_request", error_link: "https://docs.meilisearch.com/errors#index-not-found" }, details: { swaps: [IndexSwap { indexes: ("a", "b") }, IndexSwap { indexes: ("c", "e") }, IndexSwap { indexes: ("d", "f") }] }, kind: IndexSwap { swaps: [IndexSwap { indexes: ("a", "b") }, IndexSwap { indexes: ("c", "e") }, IndexSwap { indexes: ("d", "f") }] }}
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
### Status:
|
### Status:
|
||||||
enqueued []
|
enqueued []
|
||||||
|
@ -128,11 +128,11 @@ pub async fn delete_document(
|
|||||||
#[derive(Debug, DeserializeFromValue)]
|
#[derive(Debug, DeserializeFromValue)]
|
||||||
#[deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields)]
|
#[deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields)]
|
||||||
pub struct BrowseQuery {
|
pub struct BrowseQuery {
|
||||||
#[deserr(default, error = DeserrQueryParamError<InvalidDocumentFields>)]
|
#[deserr(default, error = DeserrQueryParamError<InvalidDocumentOffset>)]
|
||||||
offset: Param<usize>,
|
offset: Param<usize>,
|
||||||
#[deserr(default = Param(PAGINATION_DEFAULT_LIMIT), error = DeserrQueryParamError<InvalidDocumentLimit>)]
|
#[deserr(default = Param(PAGINATION_DEFAULT_LIMIT), error = DeserrQueryParamError<InvalidDocumentLimit>)]
|
||||||
limit: Param<usize>,
|
limit: Param<usize>,
|
||||||
#[deserr(default, error = DeserrQueryParamError<InvalidDocumentLimit>)]
|
#[deserr(default, error = DeserrQueryParamError<InvalidDocumentFields>)]
|
||||||
fields: OptionStarOrList<String>,
|
fields: OptionStarOrList<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +132,12 @@ impl Index<'_> {
|
|||||||
self.service.get(url).await
|
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);
|
let mut url = format!("/tasks?indexUids={}", self.uid);
|
||||||
if !types.is_empty() {
|
if !types.is_empty() {
|
||||||
let _ = write!(url, "&types={}", types.join(","));
|
let _ = write!(url, "&types={}", types.join(","));
|
||||||
@ -140,6 +145,9 @@ impl Index<'_> {
|
|||||||
if !statuses.is_empty() {
|
if !statuses.is_empty() {
|
||||||
let _ = write!(url, "&statuses={}", statuses.join(","));
|
let _ = write!(url, "&statuses={}", statuses.join(","));
|
||||||
}
|
}
|
||||||
|
if !canceled_by.is_empty() {
|
||||||
|
let _ = write!(url, "&canceledBy={}", canceled_by.join(","));
|
||||||
|
}
|
||||||
self.service.get(url).await
|
self.service.get(url).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,6 +163,11 @@ impl Index<'_> {
|
|||||||
self.service.get(url).await
|
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) {
|
pub async fn get_all_documents(&self, options: GetAllDocumentsOptions) -> (Value, StatusCode) {
|
||||||
let mut url = format!("/indexes/{}/documents?", urlencode(self.uid.as_ref()));
|
let mut url = format!("/indexes/{}/documents?", urlencode(self.uid.as_ref()));
|
||||||
if let Some(limit) = options.limit {
|
if let Some(limit) = options.limit {
|
||||||
@ -187,6 +200,11 @@ impl Index<'_> {
|
|||||||
self.service.post_encoded(url, serde_json::to_value(&ids).unwrap(), self.encoder).await
|
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) {
|
pub async fn settings(&self) -> (Value, StatusCode) {
|
||||||
let url = format!("/indexes/{}/settings", urlencode(self.uid.as_ref()));
|
let url = format!("/indexes/{}/settings", urlencode(self.uid.as_ref()));
|
||||||
self.service.get(url).await
|
self.service.get(url).await
|
||||||
|
@ -1077,7 +1077,7 @@ async fn batch_several_documents_addition() {
|
|||||||
futures::future::join_all(waiter).await;
|
futures::future::join_all(waiter).await;
|
||||||
index.wait_task(9).await;
|
index.wait_task(9).await;
|
||||||
|
|
||||||
let (response, _code) = index.filtered_tasks(&[], &["failed"]).await;
|
let (response, _code) = index.filtered_tasks(&[], &["failed"], &[]).await;
|
||||||
|
|
||||||
// Check if only the 6th task failed
|
// Check if only the 6th task failed
|
||||||
println!("{}", &response);
|
println!("{}", &response);
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
"###);
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
mod add_documents;
|
mod add_documents;
|
||||||
mod delete_documents;
|
mod delete_documents;
|
||||||
|
mod errors;
|
||||||
mod get_documents;
|
mod get_documents;
|
||||||
mod update_documents;
|
mod update_documents;
|
||||||
|
@ -115,7 +115,7 @@ async fn list_tasks_status_filtered() {
|
|||||||
.add_documents(serde_json::from_str(include_str!("../assets/test_set.json")).unwrap(), None)
|
.add_documents(serde_json::from_str(include_str!("../assets/test_set.json")).unwrap(), None)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let (response, code) = index.filtered_tasks(&[], &["succeeded"]).await;
|
let (response, code) = index.filtered_tasks(&[], &["succeeded"], &[]).await;
|
||||||
assert_eq!(code, 200, "{}", response);
|
assert_eq!(code, 200, "{}", response);
|
||||||
assert_eq!(response["results"].as_array().unwrap().len(), 1);
|
assert_eq!(response["results"].as_array().unwrap().len(), 1);
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ async fn list_tasks_status_filtered() {
|
|||||||
|
|
||||||
index.wait_task(1).await;
|
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!(code, 200, "{}", response);
|
||||||
assert_eq!(response["results"].as_array().unwrap().len(), 2);
|
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)
|
.add_documents(serde_json::from_str(include_str!("../assets/test_set.json")).unwrap(), None)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let (response, code) = index.filtered_tasks(&["indexCreation"], &[]).await;
|
let (response, code) = index.filtered_tasks(&["indexCreation"], &[], &[]).await;
|
||||||
assert_eq!(code, 200, "{}", response);
|
assert_eq!(code, 200, "{}", response);
|
||||||
assert_eq!(response["results"].as_array().unwrap().len(), 1);
|
assert_eq!(response["results"].as_array().unwrap().len(), 1);
|
||||||
|
|
||||||
let (response, code) =
|
let (response, code) =
|
||||||
index.filtered_tasks(&["indexCreation", "documentAdditionOrUpdate"], &[]).await;
|
index.filtered_tasks(&["indexCreation", "documentAdditionOrUpdate"], &[], &[]).await;
|
||||||
assert_eq!(code, 200, "{}", response);
|
assert_eq!(code, 200, "{}", response);
|
||||||
assert_eq!(response["results"].as_array().unwrap().len(), 2);
|
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]
|
#[actix_rt::test]
|
||||||
async fn list_tasks_status_and_type_filtered() {
|
async fn list_tasks_status_and_type_filtered() {
|
||||||
let server = Server::new().await;
|
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)
|
.add_documents(serde_json::from_str(include_str!("../assets/test_set.json")).unwrap(), None)
|
||||||
.await;
|
.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!(code, 200, "{}", response);
|
||||||
assert_eq!(response["results"].as_array().unwrap().len(), 0);
|
assert_eq!(response["results"].as_array().unwrap().len(), 0);
|
||||||
|
|
||||||
@ -169,6 +184,7 @@ async fn list_tasks_status_and_type_filtered() {
|
|||||||
.filtered_tasks(
|
.filtered_tasks(
|
||||||
&["indexCreation", "documentAdditionOrUpdate"],
|
&["indexCreation", "documentAdditionOrUpdate"],
|
||||||
&["succeeded", "processing", "enqueued"],
|
&["succeeded", "processing", "enqueued"],
|
||||||
|
&[],
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert_eq!(code, 200, "{}", response);
|
assert_eq!(code, 200, "{}", response);
|
||||||
@ -844,9 +860,9 @@ async fn test_summarized_index_swap() {
|
|||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Indexes `cattos`, `doggos` not found.",
|
"message": "Indexes `cattos`, `doggos` not found.",
|
||||||
"code": "invalid_swap_indexes",
|
"code": "index_not_found",
|
||||||
"type": "invalid_request",
|
"type": "invalid_request",
|
||||||
"link": "https://docs.meilisearch.com/errors#invalid-swap-indexes"
|
"link": "https://docs.meilisearch.com/errors#index-not-found"
|
||||||
},
|
},
|
||||||
"duration": "[duration]",
|
"duration": "[duration]",
|
||||||
"enqueuedAt": "[date]",
|
"enqueuedAt": "[date]",
|
||||||
|
Loading…
Reference in New Issue
Block a user