From e5ef5a6f9ce5d54654aeff6e0c51214c3290fcaf Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Tue, 17 May 2022 11:13:29 +0200 Subject: [PATCH 01/11] Remove an unused updates.rs file --- meilisearch-http/src/routes/indexes/updates.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 meilisearch-http/src/routes/indexes/updates.rs diff --git a/meilisearch-http/src/routes/indexes/updates.rs b/meilisearch-http/src/routes/indexes/updates.rs deleted file mode 100644 index e69de29bb..000000000 From d2f457a07632887c3e4bfc0c7645dd6dc2f3efa3 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Tue, 17 May 2022 11:17:32 +0200 Subject: [PATCH 02/11] Rename the uid to taskUid in asynchronous response --- meilisearch-http/src/task.rs | 4 ++-- meilisearch-http/tests/auth/authorization.rs | 12 ++++++------ meilisearch-http/tests/common/index.rs | 2 +- meilisearch-http/tests/documents/add_documents.rs | 6 +++--- meilisearch-http/tests/index/delete_index.rs | 4 ++-- meilisearch-http/tests/index/stats.rs | 2 +- meilisearch-http/tests/settings/get_settings.rs | 2 +- meilisearch-http/tests/stats/mod.rs | 2 +- meilisearch-http/tests/tasks/mod.rs | 2 +- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/meilisearch-http/src/task.rs b/meilisearch-http/src/task.rs index 397fed618..a916d5ce8 100644 --- a/meilisearch-http/src/task.rs +++ b/meilisearch-http/src/task.rs @@ -349,7 +349,7 @@ impl From> for TaskListView { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct SummarizedTaskView { - uid: TaskId, + task_uid: TaskId, index_uid: Option, status: TaskStatus, #[serde(rename = "type")] @@ -372,7 +372,7 @@ impl From for SummarizedTaskView { }; Self { - uid: other.id, + task_uid: other.id, index_uid: other.index_uid.map(|u| u.into_inner()), status: TaskStatus::Enqueued, task_type: other.content.into(), diff --git a/meilisearch-http/tests/auth/authorization.rs b/meilisearch-http/tests/auth/authorization.rs index 25f32eb12..7d7ec1899 100644 --- a/meilisearch-http/tests/auth/authorization.rs +++ b/meilisearch-http/tests/auth/authorization.rs @@ -523,7 +523,7 @@ async fn error_creating_index_without_action() { let (response, code) = index.add_documents(documents, None).await; assert_eq!(code, 202, "{:?}", response); - let task_id = response["uid"].as_u64().unwrap(); + let task_id = response["taskUid"].as_u64().unwrap(); let response = index.wait_task(task_id).await; assert_eq!(response["status"], "failed"); @@ -534,7 +534,7 @@ async fn error_creating_index_without_action() { let (response, code) = index.update_settings(settings).await; assert_eq!(code, 202); - let task_id = response["uid"].as_u64().unwrap(); + let task_id = response["taskUid"].as_u64().unwrap(); let response = index.wait_task(task_id).await; @@ -544,7 +544,7 @@ async fn error_creating_index_without_action() { // try to create a index via add specialized settings route let (response, code) = index.update_distinct_attribute(json!("test")).await; assert_eq!(code, 202); - let task_id = response["uid"].as_u64().unwrap(); + let task_id = response["taskUid"].as_u64().unwrap(); let response = index.wait_task(task_id).await; @@ -583,7 +583,7 @@ async fn lazy_create_index() { let (response, code) = index.add_documents(documents, None).await; assert_eq!(code, 202, "{:?}", response); - let task_id = response["uid"].as_u64().unwrap(); + let task_id = response["taskUid"].as_u64().unwrap(); index.wait_task(task_id).await; @@ -597,7 +597,7 @@ async fn lazy_create_index() { let (response, code) = index.update_settings(settings).await; assert_eq!(code, 202); - let task_id = response["uid"].as_u64().unwrap(); + let task_id = response["taskUid"].as_u64().unwrap(); index.wait_task(task_id).await; @@ -609,7 +609,7 @@ async fn lazy_create_index() { let index = server.index("test2"); let (response, code) = index.update_distinct_attribute(json!("test")).await; assert_eq!(code, 202); - let task_id = response["uid"].as_u64().unwrap(); + let task_id = response["taskUid"].as_u64().unwrap(); index.wait_task(task_id).await; diff --git a/meilisearch-http/tests/common/index.rs b/meilisearch-http/tests/common/index.rs index 6c44ea369..b0c7a3342 100644 --- a/meilisearch-http/tests/common/index.rs +++ b/meilisearch-http/tests/common/index.rs @@ -46,7 +46,7 @@ impl Index<'_> { .post_str(url, include_str!("../assets/test_set.json")) .await; assert_eq!(code, 202); - let update_id = response["uid"].as_i64().unwrap(); + let update_id = response["taskUid"].as_i64().unwrap(); self.wait_task(update_id as u64).await; update_id as u64 } diff --git a/meilisearch-http/tests/documents/add_documents.rs b/meilisearch-http/tests/documents/add_documents.rs index 0ac0436dc..238df6332 100644 --- a/meilisearch-http/tests/documents/add_documents.rs +++ b/meilisearch-http/tests/documents/add_documents.rs @@ -35,7 +35,7 @@ async fn add_documents_test_json_content_types() { let body = test::read_body(res).await; let response: Value = serde_json::from_slice(&body).unwrap_or_default(); assert_eq!(status_code, 202); - assert_eq!(response["uid"], 0); + assert_eq!(response["taskUid"], 0); // put let req = test::TestRequest::put() @@ -48,7 +48,7 @@ async fn add_documents_test_json_content_types() { let body = test::read_body(res).await; let response: Value = serde_json::from_slice(&body).unwrap_or_default(); assert_eq!(status_code, 202); - assert_eq!(response["uid"], 1); + assert_eq!(response["taskUid"], 1); } /// any other content-type is must be refused @@ -599,7 +599,7 @@ async fn add_documents_no_index_creation() { let (response, code) = index.add_documents(documents, None).await; assert_eq!(code, 202); - assert_eq!(response["uid"], 0); + assert_eq!(response["taskUid"], 0); /* * currently we don’t check these field to stay ISO with meilisearch * assert_eq!(response["status"], "pending"); diff --git a/meilisearch-http/tests/index/delete_index.rs b/meilisearch-http/tests/index/delete_index.rs index 0674d0afd..f3cdf6631 100644 --- a/meilisearch-http/tests/index/delete_index.rs +++ b/meilisearch-http/tests/index/delete_index.rs @@ -52,10 +52,10 @@ async fn loop_delete_add_documents() { let mut tasks = Vec::new(); for _ in 0..50 { let (response, code) = index.add_documents(documents.clone(), None).await; - tasks.push(response["uid"].as_u64().unwrap()); + tasks.push(response["taskUid"].as_u64().unwrap()); assert_eq!(code, 202, "{}", response); let (response, code) = index.delete().await; - tasks.push(response["uid"].as_u64().unwrap()); + tasks.push(response["taskUid"].as_u64().unwrap()); assert_eq!(code, 202, "{}", response); } diff --git a/meilisearch-http/tests/index/stats.rs b/meilisearch-http/tests/index/stats.rs index 555c7311a..f55998998 100644 --- a/meilisearch-http/tests/index/stats.rs +++ b/meilisearch-http/tests/index/stats.rs @@ -35,7 +35,7 @@ async fn stats() { let (response, code) = index.add_documents(documents, None).await; assert_eq!(code, 202); - assert_eq!(response["uid"], 1); + assert_eq!(response["taskUid"], 1); index.wait_task(1).await; diff --git a/meilisearch-http/tests/settings/get_settings.rs b/meilisearch-http/tests/settings/get_settings.rs index 98b4f9558..9b3c31b63 100644 --- a/meilisearch-http/tests/settings/get_settings.rs +++ b/meilisearch-http/tests/settings/get_settings.rs @@ -122,7 +122,7 @@ async fn reset_all_settings() { let (response, code) = index.add_documents(documents, None).await; assert_eq!(code, 202); - assert_eq!(response["uid"], 0); + assert_eq!(response["taskUid"], 0); index.wait_task(0).await; index diff --git a/meilisearch-http/tests/stats/mod.rs b/meilisearch-http/tests/stats/mod.rs index b9d185ca3..0629c2e29 100644 --- a/meilisearch-http/tests/stats/mod.rs +++ b/meilisearch-http/tests/stats/mod.rs @@ -54,7 +54,7 @@ async fn stats() { let (response, code) = index.add_documents(documents, None).await; assert_eq!(code, 202, "{}", response); - assert_eq!(response["uid"], 1); + assert_eq!(response["taskUid"], 1); index.wait_task(1).await; diff --git a/meilisearch-http/tests/tasks/mod.rs b/meilisearch-http/tests/tasks/mod.rs index 167b7b05f..6f64a8970 100644 --- a/meilisearch-http/tests/tasks/mod.rs +++ b/meilisearch-http/tests/tasks/mod.rs @@ -94,7 +94,7 @@ async fn list_tasks() { macro_rules! assert_valid_summarized_task { ($response:expr, $task_type:literal, $index:literal) => {{ assert_eq!($response.as_object().unwrap().len(), 5); - assert!($response["uid"].as_u64().is_some()); + assert!($response["taskUid"].as_u64().is_some()); assert_eq!($response["indexUid"], $index); assert_eq!($response["status"], "enqueued"); assert_eq!($response["type"], $task_type); From 80f7d873563e7f06acde532d4345f4a866390a14 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Tue, 17 May 2022 10:51:28 +0200 Subject: [PATCH 03/11] Remove the /indexes/:indexUid/tasks/... routes --- meilisearch-http/src/routes/indexes/mod.rs | 2 - meilisearch-http/src/routes/indexes/tasks.rs | 80 -------------------- 2 files changed, 82 deletions(-) delete mode 100644 meilisearch-http/src/routes/indexes/tasks.rs diff --git a/meilisearch-http/src/routes/indexes/mod.rs b/meilisearch-http/src/routes/indexes/mod.rs index bd74fd724..956761eb3 100644 --- a/meilisearch-http/src/routes/indexes/mod.rs +++ b/meilisearch-http/src/routes/indexes/mod.rs @@ -15,7 +15,6 @@ use crate::task::SummarizedTaskView; pub mod documents; pub mod search; pub mod settings; -pub mod tasks; pub fn configure(cfg: &mut web::ServiceConfig) { cfg.service( @@ -34,7 +33,6 @@ pub fn configure(cfg: &mut web::ServiceConfig) { .service(web::resource("/stats").route(web::get().to(SeqHandler(get_index_stats)))) .service(web::scope("/documents").configure(documents::configure)) .service(web::scope("/search").configure(search::configure)) - .service(web::scope("/tasks").configure(tasks::configure)) .service(web::scope("/settings").configure(settings::configure)), ); } diff --git a/meilisearch-http/src/routes/indexes/tasks.rs b/meilisearch-http/src/routes/indexes/tasks.rs deleted file mode 100644 index 01ed85db8..000000000 --- a/meilisearch-http/src/routes/indexes/tasks.rs +++ /dev/null @@ -1,80 +0,0 @@ -use actix_web::{web, HttpRequest, HttpResponse}; -use log::debug; -use meilisearch_error::ResponseError; -use meilisearch_lib::MeiliSearch; -use serde::{Deserialize, Serialize}; -use serde_json::json; -use time::OffsetDateTime; - -use crate::analytics::Analytics; -use crate::extractors::authentication::{policies::*, GuardedData}; -use crate::extractors::sequential_extractor::SeqHandler; -use crate::task::{TaskListView, TaskView}; - -pub fn configure(cfg: &mut web::ServiceConfig) { - cfg.service(web::resource("").route(web::get().to(SeqHandler(get_all_tasks_status)))) - .service(web::resource("{task_id}").route(web::get().to(SeqHandler(get_task_status)))); -} - -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct UpdateIndexResponse { - name: String, - uid: String, - #[serde(serialize_with = "time::serde::rfc3339::serialize")] - created_at: OffsetDateTime, - #[serde(serialize_with = "time::serde::rfc3339::serialize")] - updated_at: OffsetDateTime, - #[serde(serialize_with = "time::serde::rfc3339::serialize")] - primary_key: OffsetDateTime, -} - -#[derive(Deserialize)] -pub struct UpdateParam { - index_uid: String, - task_id: u64, -} - -pub async fn get_task_status( - meilisearch: GuardedData, MeiliSearch>, - index_uid: web::Path, - req: HttpRequest, - analytics: web::Data, -) -> Result { - analytics.publish( - "Index Tasks Seen".to_string(), - json!({ "per_task_uid": true }), - Some(&req), - ); - - let UpdateParam { index_uid, task_id } = index_uid.into_inner(); - - let task: TaskView = meilisearch.get_index_task(index_uid, task_id).await?.into(); - - debug!("returns: {:?}", task); - Ok(HttpResponse::Ok().json(task)) -} - -pub async fn get_all_tasks_status( - meilisearch: GuardedData, MeiliSearch>, - index_uid: web::Path, - req: HttpRequest, - analytics: web::Data, -) -> Result { - analytics.publish( - "Index Tasks Seen".to_string(), - json!({ "per_task_uid": false }), - Some(&req), - ); - - let tasks: TaskListView = meilisearch - .list_index_task(index_uid.into_inner(), None, None) - .await? - .into_iter() - .map(TaskView::from) - .collect::>() - .into(); - - debug!("returns: {:?}", tasks); - Ok(HttpResponse::Ok().json(tasks)) -} From 3684c822f11c882fe4682051e8357c92efca3d22 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Tue, 17 May 2022 16:08:23 +0200 Subject: [PATCH 04/11] Add indexUid filtering on the /tasks route --- Cargo.lock | 10 ++++ meilisearch-http/Cargo.toml | 1 + meilisearch-http/src/lib.rs | 2 +- meilisearch-http/src/routes/tasks.rs | 49 ++++++++++++++++---- meilisearch-http/src/task.rs | 43 +++++++++++++++-- meilisearch-http/tests/auth/authorization.rs | 4 +- meilisearch-http/tests/common/index.rs | 4 +- meilisearch-http/tests/tasks/mod.rs | 32 ------------- meilisearch-lib/src/index_controller/mod.rs | 3 +- meilisearch-lib/src/index_resolver/mod.rs | 9 ++++ meilisearch-lib/src/lib.rs | 2 +- 11 files changed, 106 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index abdac2c1c..39eb78987 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2046,6 +2046,7 @@ dependencies = [ "rustls-pemfile", "segment", "serde", + "serde-cs", "serde_json", "serde_url_params", "sha-1", @@ -3085,6 +3086,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-cs" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d5b0435c9139761fbe5abeb1283234bcfbde88fadc2ae432579648fbce72ad" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.136" diff --git a/meilisearch-http/Cargo.toml b/meilisearch-http/Cargo.toml index 0a5cdff7f..75d0ac06e 100644 --- a/meilisearch-http/Cargo.toml +++ b/meilisearch-http/Cargo.toml @@ -62,6 +62,7 @@ rustls = "0.20.4" rustls-pemfile = "0.3.0" segment = { version = "0.2.0", optional = true } serde = { version = "1.0.136", features = ["derive"] } +serde-cs = "0.2.2" serde_json = { version = "1.0.79", features = ["preserve_order"] } sha2 = "0.10.2" siphasher = "0.3.10" diff --git a/meilisearch-http/src/lib.rs b/meilisearch-http/src/lib.rs index d1f5d9da1..201013bc6 100644 --- a/meilisearch-http/src/lib.rs +++ b/meilisearch-http/src/lib.rs @@ -2,7 +2,7 @@ #[macro_use] pub mod error; pub mod analytics; -mod task; +pub mod task; #[macro_use] pub mod extractors; pub mod helpers; diff --git a/meilisearch-http/src/routes/tasks.rs b/meilisearch-http/src/routes/tasks.rs index ae932253a..64929d5e0 100644 --- a/meilisearch-http/src/routes/tasks.rs +++ b/meilisearch-http/src/routes/tasks.rs @@ -2,21 +2,33 @@ use actix_web::{web, HttpRequest, HttpResponse}; use meilisearch_error::ResponseError; use meilisearch_lib::tasks::task::TaskId; use meilisearch_lib::tasks::TaskFilter; -use meilisearch_lib::MeiliSearch; +use meilisearch_lib::{IndexUid, MeiliSearch}; +use serde::Deserialize; +use serde_cs::vec::CS; use serde_json::json; use crate::analytics::Analytics; use crate::extractors::authentication::{policies::*, GuardedData}; use crate::extractors::sequential_extractor::SeqHandler; -use crate::task::{TaskListView, TaskView}; +use crate::task::{TaskListView, TaskStatus, TaskType, TaskView}; pub fn configure(cfg: &mut web::ServiceConfig) { cfg.service(web::resource("").route(web::get().to(SeqHandler(get_tasks)))) .service(web::resource("/{task_id}").route(web::get().to(SeqHandler(get_task)))); } +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct TasksFilter { + #[serde(rename = "type")] + type_: Option>, + status: Option>, + index_uid: Option>, +} + async fn get_tasks( meilisearch: GuardedData, MeiliSearch>, + params: web::Query, req: HttpRequest, analytics: web::Data, ) -> Result { @@ -26,15 +38,34 @@ async fn get_tasks( Some(&req), ); + let TasksFilter { + type_, + status, + index_uid, + } = params.into_inner(); + let search_rules = &meilisearch.filters().search_rules; - let filters = if search_rules.is_index_authorized("*") { - None - } else { - let mut filters = TaskFilter::default(); - for (index, _policy) in search_rules.clone() { - filters.filter_index(index); + let filters = match index_uid { + Some(indexes) => { + let mut filters = TaskFilter::default(); + for name in indexes.into_inner() { + if search_rules.is_index_authorized(&name) { + filters.filter_index(name.to_string()); + } + } + Some(filters) + } + None => { + if search_rules.is_index_authorized("*") { + None + } else { + let mut filters = TaskFilter::default(); + for (index, _policy) in search_rules.clone() { + filters.filter_index(index); + } + Some(filters) + } } - Some(filters) }; let tasks: TaskListView = meilisearch diff --git a/meilisearch-http/src/task.rs b/meilisearch-http/src/task.rs index a916d5ce8..0c22b8ed6 100644 --- a/meilisearch-http/src/task.rs +++ b/meilisearch-http/src/task.rs @@ -1,4 +1,5 @@ use std::fmt::Write; +use std::str::FromStr; use std::write; use meilisearch_error::ResponseError; @@ -8,14 +9,14 @@ use meilisearch_lib::tasks::batch::BatchId; use meilisearch_lib::tasks::task::{ DocumentDeletion, Task, TaskContent, TaskEvent, TaskId, TaskResult, }; -use serde::{Serialize, Serializer}; +use serde::{Deserialize, Serialize, Serializer}; use time::{Duration, OffsetDateTime}; use crate::AUTOBATCHING_ENABLED; -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -enum TaskType { +pub enum TaskType { IndexCreation, IndexUpdate, IndexDeletion, @@ -50,15 +51,47 @@ impl From for TaskType { } } -#[derive(Debug, Serialize)] +impl FromStr for TaskType { + type Err = &'static str; + + fn from_str(status: &str) -> Result { + match status { + "indexCreation" => Ok(TaskType::IndexCreation), + "indexUpdate" => Ok(TaskType::IndexUpdate), + "indexDeletion" => Ok(TaskType::IndexDeletion), + "documentAddition" => Ok(TaskType::DocumentAddition), + "documentPartial" => Ok(TaskType::DocumentPartial), + "documentDeletion" => Ok(TaskType::DocumentDeletion), + "settingsUpdate" => Ok(TaskType::SettingsUpdate), + "clearAll" => Ok(TaskType::ClearAll), + _ => Err("invalid task type value"), + } + } +} + +#[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -enum TaskStatus { +pub enum TaskStatus { Enqueued, Processing, Succeeded, Failed, } +impl FromStr for TaskStatus { + type Err = &'static str; + + fn from_str(status: &str) -> Result { + match status { + "enqueued" => Ok(TaskStatus::Enqueued), + "processing" => Ok(TaskStatus::Processing), + "succeeded" => Ok(TaskStatus::Succeeded), + "failed" => Ok(TaskStatus::Failed), + _ => Err("invalid task status value"), + } + } +} + #[derive(Debug, Serialize)] #[serde(untagged)] #[allow(clippy::large_enum_variant)] diff --git a/meilisearch-http/tests/auth/authorization.rs b/meilisearch-http/tests/auth/authorization.rs index 7d7ec1899..56a1a13ca 100644 --- a/meilisearch-http/tests/auth/authorization.rs +++ b/meilisearch-http/tests/auth/authorization.rs @@ -16,8 +16,8 @@ pub static AUTHORIZATIONS: Lazy hashset!{"documents.get", "*"}, ("DELETE", "/indexes/products/documents/0") => hashset!{"documents.delete", "*"}, ("GET", "/tasks") => hashset!{"tasks.get", "*"}, - ("GET", "/indexes/products/tasks") => hashset!{"tasks.get", "*"}, - ("GET", "/indexes/products/tasks/0") => hashset!{"tasks.get", "*"}, + ("GET", "/tasks?indexUid=products") => hashset!{"tasks.get", "*"}, + ("GET", "/tasks/0") => hashset!{"tasks.get", "*"}, ("PUT", "/indexes/products/") => hashset!{"indexes.update", "*"}, ("GET", "/indexes/products/") => hashset!{"indexes.get", "*"}, ("DELETE", "/indexes/products/") => hashset!{"indexes.delete", "*"}, diff --git a/meilisearch-http/tests/common/index.rs b/meilisearch-http/tests/common/index.rs index b0c7a3342..9e86ac27e 100644 --- a/meilisearch-http/tests/common/index.rs +++ b/meilisearch-http/tests/common/index.rs @@ -122,12 +122,12 @@ impl Index<'_> { } pub async fn get_task(&self, update_id: u64) -> (Value, StatusCode) { - let url = format!("/indexes/{}/tasks/{}", self.uid, update_id); + let url = format!("/tasks/{}", update_id); self.service.get(url).await } pub async fn list_tasks(&self) -> (Value, StatusCode) { - let url = format!("/indexes/{}/tasks", self.uid); + let url = format!("/tasks?indexUid={}", self.uid); self.service.get(url).await } diff --git a/meilisearch-http/tests/tasks/mod.rs b/meilisearch-http/tests/tasks/mod.rs index 6f64a8970..ce0f56eb5 100644 --- a/meilisearch-http/tests/tasks/mod.rs +++ b/meilisearch-http/tests/tasks/mod.rs @@ -3,22 +3,6 @@ use serde_json::json; use time::format_description::well_known::Rfc3339; use time::OffsetDateTime; -#[actix_rt::test] -async fn error_get_task_unexisting_index() { - let server = Server::new().await; - let (response, code) = server.service.get("/indexes/test/tasks").await; - - let expected_response = json!({ - "message": "Index `test` not found.", - "code": "index_not_found", - "type": "invalid_request", - "link": "https://docs.meilisearch.com/errors#index_not_found" - }); - - assert_eq!(response, expected_response); - assert_eq!(code, 404); -} - #[actix_rt::test] async fn error_get_unexisting_task_status() { let server = Server::new().await; @@ -58,22 +42,6 @@ async fn get_task_status() { // TODO check resonse format, as per #48 } -#[actix_rt::test] -async fn error_list_tasks_unexisting_index() { - let server = Server::new().await; - let (response, code) = server.index("test").list_tasks().await; - - let expected_response = json!({ - "message": "Index `test` not found.", - "code": "index_not_found", - "type": "invalid_request", - "link": "https://docs.meilisearch.com/errors#index_not_found" - }); - - assert_eq!(response, expected_response); - assert_eq!(code, 404); -} - #[actix_rt::test] async fn list_tasks() { let server = Server::new().await; diff --git a/meilisearch-lib/src/index_controller/mod.rs b/meilisearch-lib/src/index_controller/mod.rs index 30a6b6dc8..7ec159684 100644 --- a/meilisearch-lib/src/index_controller/mod.rs +++ b/meilisearch-lib/src/index_controller/mod.rs @@ -35,7 +35,8 @@ use error::Result; use self::error::IndexControllerError; use crate::index_resolver::index_store::{IndexStore, MapIndexStore}; use crate::index_resolver::meta_store::{HeedMetaStore, IndexMetaStore}; -use crate::index_resolver::{create_index_resolver, IndexResolver, IndexUid}; +pub use crate::index_resolver::IndexUid; +use crate::index_resolver::{create_index_resolver, IndexResolver}; use crate::update_file_store::UpdateFileStore; pub mod error; diff --git a/meilisearch-lib/src/index_resolver/mod.rs b/meilisearch-lib/src/index_resolver/mod.rs index cc0308f9e..1900061c7 100644 --- a/meilisearch-lib/src/index_resolver/mod.rs +++ b/meilisearch-lib/src/index_resolver/mod.rs @@ -4,6 +4,7 @@ pub mod meta_store; use std::convert::{TryFrom, TryInto}; use std::path::Path; +use std::str::FromStr; use std::sync::Arc; use error::{IndexResolverError, Result}; @@ -88,6 +89,14 @@ impl TryInto for String { } } +impl FromStr for IndexUid { + type Err = IndexResolverError; + + fn from_str(s: &str) -> Result { + IndexUid::new(s.to_string()) + } +} + pub struct IndexResolver { index_uuid_store: U, index_store: I, diff --git a/meilisearch-lib/src/lib.rs b/meilisearch-lib/src/lib.rs index 3d3d5e860..52da63027 100644 --- a/meilisearch-lib/src/lib.rs +++ b/meilisearch-lib/src/lib.rs @@ -13,7 +13,7 @@ mod update_file_store; use std::path::Path; -pub use index_controller::MeiliSearch; +pub use index_controller::{IndexUid, MeiliSearch}; pub use milli; pub use milli::heed; From 8509243e682b0317630f491cdd2605f424d3eaa3 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Wed, 18 May 2022 12:07:06 +0200 Subject: [PATCH 05/11] Implement the status and type filtering on the tasks route --- meilisearch-http/src/routes/tasks.rs | 85 ++++++++++++++++++++++++-- meilisearch-http/src/task.rs | 21 +++++-- meilisearch-http/tests/common/index.rs | 11 ++++ meilisearch-http/tests/tasks/mod.rs | 79 ++++++++++++++++++++++++ 4 files changed, 185 insertions(+), 11 deletions(-) diff --git a/meilisearch-http/src/routes/tasks.rs b/meilisearch-http/src/routes/tasks.rs index 64929d5e0..02f700ccd 100644 --- a/meilisearch-http/src/routes/tasks.rs +++ b/meilisearch-http/src/routes/tasks.rs @@ -1,6 +1,7 @@ use actix_web::{web, HttpRequest, HttpResponse}; use meilisearch_error::ResponseError; -use meilisearch_lib::tasks::task::TaskId; +use meilisearch_lib::milli::update::IndexDocumentsMethod; +use meilisearch_lib::tasks::task::{DocumentDeletion, TaskContent, TaskEvent, TaskId}; use meilisearch_lib::tasks::TaskFilter; use meilisearch_lib::{IndexUid, MeiliSearch}; use serde::Deserialize; @@ -19,16 +20,51 @@ pub fn configure(cfg: &mut web::ServiceConfig) { #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct TasksFilter { +pub struct TaskFilterQuery { #[serde(rename = "type")] type_: Option>, status: Option>, index_uid: Option>, } +#[rustfmt::skip] +fn task_type_matches_content(type_: &TaskType, content: &TaskContent) -> bool { + matches!((type_, content), + (TaskType::IndexCreation, TaskContent::IndexCreation { .. }) + | (TaskType::IndexUpdate, TaskContent::IndexUpdate { .. }) + | (TaskType::IndexDeletion, TaskContent::IndexDeletion) + | (TaskType::DocumentAddition, TaskContent::DocumentAddition { + merge_strategy: IndexDocumentsMethod::ReplaceDocuments, + .. + }) + | (TaskType::DocumentPartial, TaskContent::DocumentAddition { + merge_strategy: IndexDocumentsMethod::UpdateDocuments, + .. + }) + | (TaskType::DocumentDeletion, TaskContent::DocumentDeletion(DocumentDeletion::Ids(_))) + | (TaskType::SettingsUpdate, TaskContent::SettingsUpdate { .. }) + | (TaskType::ClearAll, TaskContent::DocumentDeletion(DocumentDeletion::Clear)) + ) +} + +fn task_status_matches_events(status: &TaskStatus, events: &[TaskEvent]) -> bool { + events.last().map_or(false, |event| { + matches!( + (status, event), + (TaskStatus::Enqueued, TaskEvent::Created(_)) + | ( + TaskStatus::Processing, + TaskEvent::Processing(_) | TaskEvent::Batched { .. } + ) + | (TaskStatus::Succeeded, TaskEvent::Succeded { .. }) + | (TaskStatus::Failed, TaskEvent::Failed { .. }), + ) + }) +} + async fn get_tasks( meilisearch: GuardedData, MeiliSearch>, - params: web::Query, + params: web::Query, req: HttpRequest, analytics: web::Data, ) -> Result { @@ -38,14 +74,17 @@ async fn get_tasks( Some(&req), ); - let TasksFilter { + let TaskFilterQuery { type_, status, index_uid, } = params.into_inner(); let search_rules = &meilisearch.filters().search_rules; - let filters = match index_uid { + + // We first filter on potential indexes and make sure + // that the search filter restrictions are also applied. + let indexes_filters = match index_uid { Some(indexes) => { let mut filters = TaskFilter::default(); for name in indexes.into_inner() { @@ -68,6 +107,42 @@ async fn get_tasks( } }; + // Then we complete the task filter with other potential status and types filters. + let filters = match (type_, status) { + (Some(CS(types)), Some(CS(statuses))) => { + let mut filters = indexes_filters.unwrap_or_default(); + filters.filter_fn(move |task| { + let matches_type = types + .iter() + .any(|t| task_type_matches_content(&t, &task.content)); + let matches_status = statuses + .iter() + .any(|s| task_status_matches_events(&s, &task.events)); + matches_type && matches_status + }); + Some(filters) + } + (Some(CS(types)), None) => { + let mut filters = indexes_filters.unwrap_or_default(); + filters.filter_fn(move |task| { + types + .iter() + .any(|t| task_type_matches_content(&t, &task.content)) + }); + Some(filters) + } + (None, Some(CS(statuses))) => { + let mut filters = indexes_filters.unwrap_or_default(); + filters.filter_fn(move |task| { + statuses + .iter() + .any(|s| task_status_matches_events(&s, &task.events)) + }); + Some(filters) + } + (None, None) => indexes_filters, + }; + let tasks: TaskListView = meilisearch .list_tasks(filters, None, None) .await? diff --git a/meilisearch-http/src/task.rs b/meilisearch-http/src/task.rs index 0c22b8ed6..4ecb6cead 100644 --- a/meilisearch-http/src/task.rs +++ b/meilisearch-http/src/task.rs @@ -52,9 +52,9 @@ impl From for TaskType { } impl FromStr for TaskType { - type Err = &'static str; + type Err = String; - fn from_str(status: &str) -> Result { + fn from_str(status: &str) -> Result { match status { "indexCreation" => Ok(TaskType::IndexCreation), "indexUpdate" => Ok(TaskType::IndexUpdate), @@ -64,7 +64,12 @@ impl FromStr for TaskType { "documentDeletion" => Ok(TaskType::DocumentDeletion), "settingsUpdate" => Ok(TaskType::SettingsUpdate), "clearAll" => Ok(TaskType::ClearAll), - _ => Err("invalid task type value"), + unknown => Err(format!( + "invalid task type `{}` value, expecting one of: \ + indexCreation, indexUpdate, indexDeletion, documentAddition, \ + documentPartial, documentDeletion, settingsUpdate, or clearAll", + unknown + )), } } } @@ -79,15 +84,19 @@ pub enum TaskStatus { } impl FromStr for TaskStatus { - type Err = &'static str; + type Err = String; - fn from_str(status: &str) -> Result { + fn from_str(status: &str) -> Result { match status { "enqueued" => Ok(TaskStatus::Enqueued), "processing" => Ok(TaskStatus::Processing), "succeeded" => Ok(TaskStatus::Succeeded), "failed" => Ok(TaskStatus::Failed), - _ => Err("invalid task status value"), + unknown => Err(format!( + "invalid task status `{}` value, expecting one of: \ + enqueued, processing, succeeded, or failed", + unknown + )), } } } diff --git a/meilisearch-http/tests/common/index.rs b/meilisearch-http/tests/common/index.rs index 9e86ac27e..bdce22db2 100644 --- a/meilisearch-http/tests/common/index.rs +++ b/meilisearch-http/tests/common/index.rs @@ -131,6 +131,17 @@ impl Index<'_> { self.service.get(url).await } + pub async fn filtered_tasks(&self, type_: &[&str], status: &[&str]) -> (Value, StatusCode) { + let mut url = format!("/tasks?indexUid={}", self.uid); + if !type_.is_empty() { + url += &format!("&type={}", type_.join(",")); + } + if !status.is_empty() { + url += &format!("&status={}", status.join(",")); + } + self.service.get(url).await + } + pub async fn get_document( &self, id: u64, diff --git a/meilisearch-http/tests/tasks/mod.rs b/meilisearch-http/tests/tasks/mod.rs index ce0f56eb5..300cddde7 100644 --- a/meilisearch-http/tests/tasks/mod.rs +++ b/meilisearch-http/tests/tasks/mod.rs @@ -59,6 +59,85 @@ async fn list_tasks() { assert_eq!(response["results"].as_array().unwrap().len(), 2); } +#[actix_rt::test] +async fn list_tasks_status_filtered() { + 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(&[], &["succeeded"]).await; + assert_eq!(code, 200, "{}", response); + assert_eq!(response["results"].as_array().unwrap().len(), 1); + + let (response, code) = index.filtered_tasks(&[], &["processing"]).await; + assert_eq!(code, 200, "{}", response); + assert_eq!(response["results"].as_array().unwrap().len(), 1); + + index.wait_task(1).await; + + let (response, code) = index.filtered_tasks(&[], &["succeeded"]).await; + assert_eq!(code, 200, "{}", response); + assert_eq!(response["results"].as_array().unwrap().len(), 2); +} + +#[actix_rt::test] +async fn list_tasks_type_filtered() { + 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(&["indexCreation"], &[]).await; + assert_eq!(code, 200, "{}", response); + assert_eq!(response["results"].as_array().unwrap().len(), 1); + + let (response, code) = index + .filtered_tasks(&["indexCreation", "documentAddition"], &[]) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!(response["results"].as_array().unwrap().len(), 2); +} + +#[actix_rt::test] +async fn list_tasks_status_and_type_filtered() { + 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(&["indexCreation"], &["failed"]).await; + assert_eq!(code, 200, "{}", response); + assert_eq!(response["results"].as_array().unwrap().len(), 0); + + let (response, code) = index + .filtered_tasks( + &["indexCreation", "documentAddition"], + &["succeeded", "processing"], + ) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!(response["results"].as_array().unwrap().len(), 2); +} + macro_rules! assert_valid_summarized_task { ($response:expr, $task_type:literal, $index:literal) => {{ assert_eq!($response.as_object().unwrap().len(), 5); From 3f80468f1827c537c0a578cc9d3f8c612203e809 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Wed, 25 May 2022 12:05:24 +0200 Subject: [PATCH 06/11] Rename the Tasks Types --- meilisearch-http/src/routes/tasks.rs | 32 ++----- meilisearch-http/src/task.rs | 94 +++++++++---------- .../tests/documents/add_documents.rs | 10 +- meilisearch-http/tests/dumps/mod.rs | 18 ++-- meilisearch-http/tests/tasks/mod.rs | 17 ++-- 5 files changed, 75 insertions(+), 96 deletions(-) diff --git a/meilisearch-http/src/routes/tasks.rs b/meilisearch-http/src/routes/tasks.rs index 02f700ccd..66f4bbbdb 100644 --- a/meilisearch-http/src/routes/tasks.rs +++ b/meilisearch-http/src/routes/tasks.rs @@ -1,7 +1,6 @@ use actix_web::{web, HttpRequest, HttpResponse}; use meilisearch_error::ResponseError; -use meilisearch_lib::milli::update::IndexDocumentsMethod; -use meilisearch_lib::tasks::task::{DocumentDeletion, TaskContent, TaskEvent, TaskId}; +use meilisearch_lib::tasks::task::{TaskContent, TaskEvent, TaskId}; use meilisearch_lib::tasks::TaskFilter; use meilisearch_lib::{IndexUid, MeiliSearch}; use serde::Deserialize; @@ -30,34 +29,23 @@ pub struct TaskFilterQuery { #[rustfmt::skip] fn task_type_matches_content(type_: &TaskType, content: &TaskContent) -> bool { matches!((type_, content), - (TaskType::IndexCreation, TaskContent::IndexCreation { .. }) + (TaskType::IndexCreation, TaskContent::IndexCreation { .. }) | (TaskType::IndexUpdate, TaskContent::IndexUpdate { .. }) | (TaskType::IndexDeletion, TaskContent::IndexDeletion) - | (TaskType::DocumentAddition, TaskContent::DocumentAddition { - merge_strategy: IndexDocumentsMethod::ReplaceDocuments, - .. - }) - | (TaskType::DocumentPartial, TaskContent::DocumentAddition { - merge_strategy: IndexDocumentsMethod::UpdateDocuments, - .. - }) - | (TaskType::DocumentDeletion, TaskContent::DocumentDeletion(DocumentDeletion::Ids(_))) + | (TaskType::DocumentAdditionOrUpdate, TaskContent::DocumentAddition { .. }) + | (TaskType::DocumentDeletion, TaskContent::DocumentDeletion(_)) | (TaskType::SettingsUpdate, TaskContent::SettingsUpdate { .. }) - | (TaskType::ClearAll, TaskContent::DocumentDeletion(DocumentDeletion::Clear)) ) } +#[rustfmt::skip] fn task_status_matches_events(status: &TaskStatus, events: &[TaskEvent]) -> bool { events.last().map_or(false, |event| { - matches!( - (status, event), - (TaskStatus::Enqueued, TaskEvent::Created(_)) - | ( - TaskStatus::Processing, - TaskEvent::Processing(_) | TaskEvent::Batched { .. } - ) - | (TaskStatus::Succeeded, TaskEvent::Succeded { .. }) - | (TaskStatus::Failed, TaskEvent::Failed { .. }), + matches!((status, event), + (TaskStatus::Enqueued, TaskEvent::Created(_)) + | (TaskStatus::Processing, TaskEvent::Processing(_) | TaskEvent::Batched { .. }) + | (TaskStatus::Succeeded, TaskEvent::Succeded { .. }) + | (TaskStatus::Failed, TaskEvent::Failed { .. }), ) }) } diff --git a/meilisearch-http/src/task.rs b/meilisearch-http/src/task.rs index 4ecb6cead..c7aaf0030 100644 --- a/meilisearch-http/src/task.rs +++ b/meilisearch-http/src/task.rs @@ -4,7 +4,6 @@ use std::write; use meilisearch_error::ResponseError; use meilisearch_lib::index::{Settings, Unchecked}; -use meilisearch_lib::milli::update::IndexDocumentsMethod; use meilisearch_lib::tasks::batch::BatchId; use meilisearch_lib::tasks::task::{ DocumentDeletion, Task, TaskContent, TaskEvent, TaskId, TaskResult, @@ -20,33 +19,22 @@ pub enum TaskType { IndexCreation, IndexUpdate, IndexDeletion, - DocumentAddition, - DocumentPartial, + DocumentAdditionOrUpdate, DocumentDeletion, SettingsUpdate, - ClearAll, DumpCreation, } impl From for TaskType { fn from(other: TaskContent) -> Self { match other { - TaskContent::DocumentAddition { - merge_strategy: IndexDocumentsMethod::ReplaceDocuments, - .. - } => TaskType::DocumentAddition, - TaskContent::DocumentAddition { - merge_strategy: IndexDocumentsMethod::UpdateDocuments, - .. - } => TaskType::DocumentPartial, - TaskContent::DocumentDeletion(DocumentDeletion::Clear) => TaskType::ClearAll, - TaskContent::DocumentDeletion(DocumentDeletion::Ids(_)) => TaskType::DocumentDeletion, - TaskContent::SettingsUpdate { .. } => TaskType::SettingsUpdate, - TaskContent::IndexDeletion => TaskType::IndexDeletion, TaskContent::IndexCreation { .. } => TaskType::IndexCreation, TaskContent::IndexUpdate { .. } => TaskType::IndexUpdate, + TaskContent::IndexDeletion => TaskType::IndexDeletion, + TaskContent::DocumentAddition { .. } => TaskType::DocumentAdditionOrUpdate, + TaskContent::DocumentDeletion(_) => TaskType::DocumentDeletion, + TaskContent::SettingsUpdate { .. } => TaskType::SettingsUpdate, TaskContent::Dump { .. } => TaskType::DumpCreation, - _ => unreachable!("unexpected task type"), } } } @@ -55,21 +43,27 @@ impl FromStr for TaskType { type Err = String; fn from_str(status: &str) -> Result { - match status { - "indexCreation" => Ok(TaskType::IndexCreation), - "indexUpdate" => Ok(TaskType::IndexUpdate), - "indexDeletion" => Ok(TaskType::IndexDeletion), - "documentAddition" => Ok(TaskType::DocumentAddition), - "documentPartial" => Ok(TaskType::DocumentPartial), - "documentDeletion" => Ok(TaskType::DocumentDeletion), - "settingsUpdate" => Ok(TaskType::SettingsUpdate), - "clearAll" => Ok(TaskType::ClearAll), - unknown => Err(format!( - "invalid task type `{}` value, expecting one of: \ - indexCreation, indexUpdate, indexDeletion, documentAddition, \ - documentPartial, documentDeletion, settingsUpdate, or clearAll", - unknown - )), + if status.eq_ignore_ascii_case("indexCreation") { + Ok(TaskType::IndexCreation) + } else if status.eq_ignore_ascii_case("indexUpdate") { + Ok(TaskType::IndexUpdate) + } else if status.eq_ignore_ascii_case("indexDeletion") { + Ok(TaskType::IndexDeletion) + } else if status.eq_ignore_ascii_case("documentAdditionOrUpdate") { + Ok(TaskType::DocumentAdditionOrUpdate) + } else if status.eq_ignore_ascii_case("documentDeletion") { + Ok(TaskType::DocumentDeletion) + } else if status.eq_ignore_ascii_case("settingsUpdate") { + Ok(TaskType::SettingsUpdate) + } else if status.eq_ignore_ascii_case("dumpCreation") { + Ok(TaskType::DumpCreation) + } else { + Err(format!( + "invalid task type `{}`, expecting one of: \ + indexCreation, indexUpdate, indexDeletion, documentAdditionOrUpdate, \ + documentDeletion, settingsUpdate, dumpCreation", + status + )) } } } @@ -87,16 +81,20 @@ impl FromStr for TaskStatus { type Err = String; fn from_str(status: &str) -> Result { - match status { - "enqueued" => Ok(TaskStatus::Enqueued), - "processing" => Ok(TaskStatus::Processing), - "succeeded" => Ok(TaskStatus::Succeeded), - "failed" => Ok(TaskStatus::Failed), - unknown => Err(format!( - "invalid task status `{}` value, expecting one of: \ + if status.eq_ignore_ascii_case("enqueued") { + Ok(TaskStatus::Enqueued) + } else if status.eq_ignore_ascii_case("processing") { + Ok(TaskStatus::Processing) + } else if status.eq_ignore_ascii_case("succeeded") { + Ok(TaskStatus::Succeeded) + } else if status.eq_ignore_ascii_case("failed") { + Ok(TaskStatus::Failed) + } else { + Err(format!( + "invalid task status `{}`, expecting one of: \ enqueued, processing, succeeded, or failed", - unknown - )), + status, + )) } } } @@ -214,22 +212,14 @@ impl From for TaskView { let (task_type, mut details) = match content { TaskContent::DocumentAddition { - merge_strategy, - documents_count, - .. + documents_count, .. } => { let details = TaskDetails::DocumentAddition { received_documents: documents_count, indexed_documents: None, }; - let task_type = match merge_strategy { - IndexDocumentsMethod::UpdateDocuments => TaskType::DocumentPartial, - IndexDocumentsMethod::ReplaceDocuments => TaskType::DocumentAddition, - _ => unreachable!("Unexpected document merge strategy."), - }; - - (task_type, Some(details)) + (TaskType::DocumentAdditionOrUpdate, Some(details)) } TaskContent::DocumentDeletion(DocumentDeletion::Ids(ids)) => ( TaskType::DocumentDeletion, @@ -239,7 +229,7 @@ impl From for TaskView { }), ), TaskContent::DocumentDeletion(DocumentDeletion::Clear) => ( - TaskType::ClearAll, + TaskType::DocumentDeletion, Some(TaskDetails::ClearAll { deleted_documents: None, }), diff --git a/meilisearch-http/tests/documents/add_documents.rs b/meilisearch-http/tests/documents/add_documents.rs index 238df6332..ab271ce18 100644 --- a/meilisearch-http/tests/documents/add_documents.rs +++ b/meilisearch-http/tests/documents/add_documents.rs @@ -615,7 +615,7 @@ async fn add_documents_no_index_creation() { assert_eq!(code, 200); assert_eq!(response["status"], "succeeded"); assert_eq!(response["uid"], 0); - assert_eq!(response["type"], "documentAddition"); + assert_eq!(response["type"], "documentAdditionOrUpdate"); assert_eq!(response["details"]["receivedDocuments"], 1); assert_eq!(response["details"]["indexedDocuments"], 1); @@ -685,7 +685,7 @@ async fn document_addition_with_primary_key() { assert_eq!(code, 200); assert_eq!(response["status"], "succeeded"); assert_eq!(response["uid"], 0); - assert_eq!(response["type"], "documentAddition"); + assert_eq!(response["type"], "documentAdditionOrUpdate"); assert_eq!(response["details"]["receivedDocuments"], 1); assert_eq!(response["details"]["indexedDocuments"], 1); @@ -714,7 +714,7 @@ async fn document_update_with_primary_key() { assert_eq!(code, 200); assert_eq!(response["status"], "succeeded"); assert_eq!(response["uid"], 0); - assert_eq!(response["type"], "documentPartial"); + assert_eq!(response["type"], "documentAdditionOrUpdate"); assert_eq!(response["details"]["indexedDocuments"], 1); assert_eq!(response["details"]["receivedDocuments"], 1); @@ -818,7 +818,7 @@ async fn add_larger_dataset() { let (response, code) = index.get_task(update_id).await; assert_eq!(code, 200); assert_eq!(response["status"], "succeeded"); - assert_eq!(response["type"], "documentAddition"); + assert_eq!(response["type"], "documentAdditionOrUpdate"); assert_eq!(response["details"]["indexedDocuments"], 77); assert_eq!(response["details"]["receivedDocuments"], 77); let (response, code) = index @@ -840,7 +840,7 @@ async fn update_larger_dataset() { index.wait_task(0).await; let (response, code) = index.get_task(0).await; assert_eq!(code, 200); - assert_eq!(response["type"], "documentPartial"); + assert_eq!(response["type"], "documentAdditionOrUpdate"); assert_eq!(response["details"]["indexedDocuments"], 77); let (response, code) = index .get_all_documents(GetAllDocumentsOptions { diff --git a/meilisearch-http/tests/dumps/mod.rs b/meilisearch-http/tests/dumps/mod.rs index 22625f17f..6d6e6494a 100644 --- a/meilisearch-http/tests/dumps/mod.rs +++ b/meilisearch-http/tests/dumps/mod.rs @@ -69,7 +69,7 @@ async fn import_dump_v2_movie_raw() { assert_eq!(code, 200); assert_eq!( tasks, - json!({ "results": [{"uid": 0, "indexUid": "indexUID", "status": "succeeded", "type": "documentAddition", "details": { "receivedDocuments": 0, "indexedDocuments": 31944 }, "duration": "PT41.751156S", "enqueuedAt": "2021-09-08T08:30:30.550282Z", "startedAt": "2021-09-08T08:30:30.553012Z", "finishedAt": "2021-09-08T08:31:12.304168Z"}]}) + json!({ "results": [{"uid": 0, "indexUid": "indexUID", "status": "succeeded", "type": "documentAdditionOrUpdate", "details": { "receivedDocuments": 0, "indexedDocuments": 31944 }, "duration": "PT41.751156S", "enqueuedAt": "2021-09-08T08:30:30.550282Z", "startedAt": "2021-09-08T08:30:30.553012Z", "finishedAt": "2021-09-08T08:31:12.304168Z"}]}) ); // finally we're just going to check that we can still get a few documents by id @@ -134,7 +134,7 @@ async fn import_dump_v2_movie_with_settings() { assert_eq!(code, 200); assert_eq!( tasks, - json!({ "results": [{ "uid": 1, "indexUid": "indexUID", "status": "succeeded", "type": "settingsUpdate", "details": { "displayedAttributes": ["title", "genres", "overview", "poster", "release_date"], "searchableAttributes": ["title", "overview"], "filterableAttributes": ["genres"], "stopWords": ["of", "the"] }, "duration": "PT37.488777S", "enqueuedAt": "2021-09-08T08:24:02.323444Z", "startedAt": "2021-09-08T08:24:02.324145Z", "finishedAt": "2021-09-08T08:24:39.812922Z" }, { "uid": 0, "indexUid": "indexUID", "status": "succeeded", "type": "documentAddition", "details": { "receivedDocuments": 0, "indexedDocuments": 31944 }, "duration": "PT39.941318S", "enqueuedAt": "2021-09-08T08:21:14.742672Z", "startedAt": "2021-09-08T08:21:14.750166Z", "finishedAt": "2021-09-08T08:21:54.691484Z" }]}) + json!({ "results": [{ "uid": 1, "indexUid": "indexUID", "status": "succeeded", "type": "settingsUpdate", "details": { "displayedAttributes": ["title", "genres", "overview", "poster", "release_date"], "searchableAttributes": ["title", "overview"], "filterableAttributes": ["genres"], "stopWords": ["of", "the"] }, "duration": "PT37.488777S", "enqueuedAt": "2021-09-08T08:24:02.323444Z", "startedAt": "2021-09-08T08:24:02.324145Z", "finishedAt": "2021-09-08T08:24:39.812922Z" }, { "uid": 0, "indexUid": "indexUID", "status": "succeeded", "type": "documentAdditionOrUpdate", "details": { "receivedDocuments": 0, "indexedDocuments": 31944 }, "duration": "PT39.941318S", "enqueuedAt": "2021-09-08T08:21:14.742672Z", "startedAt": "2021-09-08T08:21:14.750166Z", "finishedAt": "2021-09-08T08:21:54.691484Z" }]}) ); // finally we're just going to check that we can still get a few documents by id @@ -199,7 +199,7 @@ async fn import_dump_v2_rubygems_with_settings() { assert_eq!(code, 200); assert_eq!( tasks["results"][0], - json!({"uid": 92, "indexUid": "rubygems", "status": "succeeded", "type": "documentAddition", "details": {"receivedDocuments": 0, "indexedDocuments": 1042}, "duration": "PT14.034672S", "enqueuedAt": "2021-09-08T08:40:31.390775Z", "startedAt": "2021-09-08T08:51:39.060642Z", "finishedAt": "2021-09-08T08:51:53.095314Z"}) + json!({"uid": 92, "indexUid": "rubygems", "status": "succeeded", "type": "documentAdditionOrUpdate", "details": {"receivedDocuments": 0, "indexedDocuments": 1042}, "duration": "PT14.034672S", "enqueuedAt": "2021-09-08T08:40:31.390775Z", "startedAt": "2021-09-08T08:51:39.060642Z", "finishedAt": "2021-09-08T08:51:53.095314Z"}) ); assert_eq!( tasks["results"][92], @@ -268,7 +268,7 @@ async fn import_dump_v3_movie_raw() { assert_eq!(code, 200); assert_eq!( tasks, - json!({ "results": [{"uid": 0, "indexUid": "indexUID", "status": "succeeded", "type": "documentAddition", "details": { "receivedDocuments": 0, "indexedDocuments": 31944 }, "duration": "PT41.751156S", "enqueuedAt": "2021-09-08T08:30:30.550282Z", "startedAt": "2021-09-08T08:30:30.553012Z", "finishedAt": "2021-09-08T08:31:12.304168Z"}]}) + json!({ "results": [{"uid": 0, "indexUid": "indexUID", "status": "succeeded", "type": "documentAdditionOrUpdate", "details": { "receivedDocuments": 0, "indexedDocuments": 31944 }, "duration": "PT41.751156S", "enqueuedAt": "2021-09-08T08:30:30.550282Z", "startedAt": "2021-09-08T08:30:30.553012Z", "finishedAt": "2021-09-08T08:31:12.304168Z"}]}) ); // finally we're just going to check that we can still get a few documents by id @@ -333,7 +333,7 @@ async fn import_dump_v3_movie_with_settings() { assert_eq!(code, 200); assert_eq!( tasks, - json!({ "results": [{ "uid": 1, "indexUid": "indexUID", "status": "succeeded", "type": "settingsUpdate", "details": { "displayedAttributes": ["title", "genres", "overview", "poster", "release_date"], "searchableAttributes": ["title", "overview"], "filterableAttributes": ["genres"], "stopWords": ["of", "the"] }, "duration": "PT37.488777S", "enqueuedAt": "2021-09-08T08:24:02.323444Z", "startedAt": "2021-09-08T08:24:02.324145Z", "finishedAt": "2021-09-08T08:24:39.812922Z" }, { "uid": 0, "indexUid": "indexUID", "status": "succeeded", "type": "documentAddition", "details": { "receivedDocuments": 0, "indexedDocuments": 31944 }, "duration": "PT39.941318S", "enqueuedAt": "2021-09-08T08:21:14.742672Z", "startedAt": "2021-09-08T08:21:14.750166Z", "finishedAt": "2021-09-08T08:21:54.691484Z" }]}) + json!({ "results": [{ "uid": 1, "indexUid": "indexUID", "status": "succeeded", "type": "settingsUpdate", "details": { "displayedAttributes": ["title", "genres", "overview", "poster", "release_date"], "searchableAttributes": ["title", "overview"], "filterableAttributes": ["genres"], "stopWords": ["of", "the"] }, "duration": "PT37.488777S", "enqueuedAt": "2021-09-08T08:24:02.323444Z", "startedAt": "2021-09-08T08:24:02.324145Z", "finishedAt": "2021-09-08T08:24:39.812922Z" }, { "uid": 0, "indexUid": "indexUID", "status": "succeeded", "type": "documentAdditionOrUpdate", "details": { "receivedDocuments": 0, "indexedDocuments": 31944 }, "duration": "PT39.941318S", "enqueuedAt": "2021-09-08T08:21:14.742672Z", "startedAt": "2021-09-08T08:21:14.750166Z", "finishedAt": "2021-09-08T08:21:54.691484Z" }]}) ); // finally we're just going to check that we can still get a few documents by id @@ -398,7 +398,7 @@ async fn import_dump_v3_rubygems_with_settings() { assert_eq!(code, 200); assert_eq!( tasks["results"][0], - json!({"uid": 92, "indexUid": "rubygems", "status": "succeeded", "type": "documentAddition", "details": {"receivedDocuments": 0, "indexedDocuments": 1042}, "duration": "PT14.034672S", "enqueuedAt": "2021-09-08T08:40:31.390775Z", "startedAt": "2021-09-08T08:51:39.060642Z", "finishedAt": "2021-09-08T08:51:53.095314Z"}) + json!({"uid": 92, "indexUid": "rubygems", "status": "succeeded", "type": "documentAdditionOrUpdate", "details": {"receivedDocuments": 0, "indexedDocuments": 1042}, "duration": "PT14.034672S", "enqueuedAt": "2021-09-08T08:40:31.390775Z", "startedAt": "2021-09-08T08:51:39.060642Z", "finishedAt": "2021-09-08T08:51:53.095314Z"}) ); assert_eq!( tasks["results"][92], @@ -467,7 +467,7 @@ async fn import_dump_v4_movie_raw() { assert_eq!(code, 200); assert_eq!( tasks, - json!({ "results": [{"uid": 0, "indexUid": "indexUID", "status": "succeeded", "type": "documentAddition", "details": { "receivedDocuments": 0, "indexedDocuments": 31944 }, "duration": "PT41.751156S", "enqueuedAt": "2021-09-08T08:30:30.550282Z", "startedAt": "2021-09-08T08:30:30.553012Z", "finishedAt": "2021-09-08T08:31:12.304168Z"}]}) + json!({ "results": [{"uid": 0, "indexUid": "indexUID", "status": "succeeded", "type": "documentAdditionOrUpdate", "details": { "receivedDocuments": 0, "indexedDocuments": 31944 }, "duration": "PT41.751156S", "enqueuedAt": "2021-09-08T08:30:30.550282Z", "startedAt": "2021-09-08T08:30:30.553012Z", "finishedAt": "2021-09-08T08:31:12.304168Z"}]}) ); // finally we're just going to check that we can still get a few documents by id @@ -532,7 +532,7 @@ async fn import_dump_v4_movie_with_settings() { assert_eq!(code, 200); assert_eq!( tasks, - json!({ "results": [{ "uid": 1, "indexUid": "indexUID", "status": "succeeded", "type": "settingsUpdate", "details": { "displayedAttributes": ["title", "genres", "overview", "poster", "release_date"], "searchableAttributes": ["title", "overview"], "filterableAttributes": ["genres"], "stopWords": ["of", "the"] }, "duration": "PT37.488777S", "enqueuedAt": "2021-09-08T08:24:02.323444Z", "startedAt": "2021-09-08T08:24:02.324145Z", "finishedAt": "2021-09-08T08:24:39.812922Z" }, { "uid": 0, "indexUid": "indexUID", "status": "succeeded", "type": "documentAddition", "details": { "receivedDocuments": 0, "indexedDocuments": 31944 }, "duration": "PT39.941318S", "enqueuedAt": "2021-09-08T08:21:14.742672Z", "startedAt": "2021-09-08T08:21:14.750166Z", "finishedAt": "2021-09-08T08:21:54.691484Z" }]}) + json!({ "results": [{ "uid": 1, "indexUid": "indexUID", "status": "succeeded", "type": "settingsUpdate", "details": { "displayedAttributes": ["title", "genres", "overview", "poster", "release_date"], "searchableAttributes": ["title", "overview"], "filterableAttributes": ["genres"], "stopWords": ["of", "the"] }, "duration": "PT37.488777S", "enqueuedAt": "2021-09-08T08:24:02.323444Z", "startedAt": "2021-09-08T08:24:02.324145Z", "finishedAt": "2021-09-08T08:24:39.812922Z" }, { "uid": 0, "indexUid": "indexUID", "status": "succeeded", "type": "documentAdditionOrUpdate", "details": { "receivedDocuments": 0, "indexedDocuments": 31944 }, "duration": "PT39.941318S", "enqueuedAt": "2021-09-08T08:21:14.742672Z", "startedAt": "2021-09-08T08:21:14.750166Z", "finishedAt": "2021-09-08T08:21:54.691484Z" }]}) ); // finally we're just going to check that we can still get a few documents by id @@ -597,7 +597,7 @@ async fn import_dump_v4_rubygems_with_settings() { assert_eq!(code, 200); assert_eq!( tasks["results"][0], - json!({ "uid": 92, "indexUid": "rubygems", "status": "succeeded", "type": "documentAddition", "details": {"receivedDocuments": 0, "indexedDocuments": 1042}, "duration": "PT14.034672S", "enqueuedAt": "2021-09-08T08:40:31.390775Z", "startedAt": "2021-09-08T08:51:39.060642Z", "finishedAt": "2021-09-08T08:51:53.095314Z"}) + json!({ "uid": 92, "indexUid": "rubygems", "status": "succeeded", "type": "documentAdditionOrUpdate", "details": {"receivedDocuments": 0, "indexedDocuments": 1042}, "duration": "PT14.034672S", "enqueuedAt": "2021-09-08T08:40:31.390775Z", "startedAt": "2021-09-08T08:51:39.060642Z", "finishedAt": "2021-09-08T08:51:53.095314Z"}) ); assert_eq!( tasks["results"][92], diff --git a/meilisearch-http/tests/tasks/mod.rs b/meilisearch-http/tests/tasks/mod.rs index 300cddde7..80bf6cb3d 100644 --- a/meilisearch-http/tests/tasks/mod.rs +++ b/meilisearch-http/tests/tasks/mod.rs @@ -76,9 +76,10 @@ async fn list_tasks_status_filtered() { assert_eq!(code, 200, "{}", response); assert_eq!(response["results"].as_array().unwrap().len(), 1); - let (response, code) = index.filtered_tasks(&[], &["processing"]).await; - assert_eq!(code, 200, "{}", response); - assert_eq!(response["results"].as_array().unwrap().len(), 1); + // We can't be sure that the update isn't already processed so we can't test this + // let (response, code) = index.filtered_tasks(&[], &["processing"]).await; + // assert_eq!(code, 200, "{}", response); + // assert_eq!(response["results"].as_array().unwrap().len(), 1); index.wait_task(1).await; @@ -105,7 +106,7 @@ async fn list_tasks_type_filtered() { assert_eq!(response["results"].as_array().unwrap().len(), 1); let (response, code) = index - .filtered_tasks(&["indexCreation", "documentAddition"], &[]) + .filtered_tasks(&["indexCreation", "documentAdditionOrUpdate"], &[]) .await; assert_eq!(code, 200, "{}", response); assert_eq!(response["results"].as_array().unwrap().len(), 2); @@ -130,7 +131,7 @@ async fn list_tasks_status_and_type_filtered() { let (response, code) = index .filtered_tasks( - &["indexCreation", "documentAddition"], + &["indexCreation", "documentAdditionOrUpdate"], &["succeeded", "processing"], ) .await; @@ -166,16 +167,16 @@ async fn test_summarized_task_view() { assert_valid_summarized_task!(response, "settingsUpdate", "test"); let (response, _) = index.update_documents(json!([{"id": 1}]), None).await; - assert_valid_summarized_task!(response, "documentPartial", "test"); + assert_valid_summarized_task!(response, "documentAdditionOrUpdate", "test"); let (response, _) = index.add_documents(json!([{"id": 1}]), None).await; - assert_valid_summarized_task!(response, "documentAddition", "test"); + assert_valid_summarized_task!(response, "documentAdditionOrUpdate", "test"); let (response, _) = index.delete_document(1).await; assert_valid_summarized_task!(response, "documentDeletion", "test"); let (response, _) = index.clear_all_documents().await; - assert_valid_summarized_task!(response, "clearAll", "test"); + assert_valid_summarized_task!(response, "documentDeletion", "test"); let (response, _) = index.delete().await; assert_valid_summarized_task!(response, "indexDeletion", "test"); From 36d94257d8b6850dbd222180df8d0d969c05d439 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Wed, 25 May 2022 18:21:50 +0200 Subject: [PATCH 07/11] Make clippy happy --- meilisearch-http/src/routes/tasks.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/meilisearch-http/src/routes/tasks.rs b/meilisearch-http/src/routes/tasks.rs index 66f4bbbdb..34132db0d 100644 --- a/meilisearch-http/src/routes/tasks.rs +++ b/meilisearch-http/src/routes/tasks.rs @@ -102,10 +102,10 @@ async fn get_tasks( filters.filter_fn(move |task| { let matches_type = types .iter() - .any(|t| task_type_matches_content(&t, &task.content)); + .any(|t| task_type_matches_content(t, &task.content)); let matches_status = statuses .iter() - .any(|s| task_status_matches_events(&s, &task.events)); + .any(|s| task_status_matches_events(s, &task.events)); matches_type && matches_status }); Some(filters) @@ -115,7 +115,7 @@ async fn get_tasks( filters.filter_fn(move |task| { types .iter() - .any(|t| task_type_matches_content(&t, &task.content)) + .any(|t| task_type_matches_content(t, &task.content)) }); Some(filters) } @@ -124,7 +124,7 @@ async fn get_tasks( filters.filter_fn(move |task| { statuses .iter() - .any(|s| task_status_matches_events(&s, &task.events)) + .any(|s| task_status_matches_events(s, &task.events)) }); Some(filters) } From b82c86c8f5fb49e3f52ec30182779ba1c6a219a3 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Mon, 30 May 2022 13:59:27 +0200 Subject: [PATCH 08/11] Allow users to filter indexUid with a * --- meilisearch-http/src/routes/tasks.rs | 43 ++++++++++++++++++++++++++-- meilisearch-http/tests/tasks/mod.rs | 25 ++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/meilisearch-http/src/routes/tasks.rs b/meilisearch-http/src/routes/tasks.rs index 34132db0d..93af5af26 100644 --- a/meilisearch-http/src/routes/tasks.rs +++ b/meilisearch-http/src/routes/tasks.rs @@ -6,6 +6,7 @@ use meilisearch_lib::{IndexUid, MeiliSearch}; use serde::Deserialize; use serde_cs::vec::CS; use serde_json::json; +use std::str::FromStr; use crate::analytics::Analytics; use crate::extractors::authentication::{policies::*, GuardedData}; @@ -23,7 +24,26 @@ pub struct TaskFilterQuery { #[serde(rename = "type")] type_: Option>, status: Option>, - index_uid: Option>, + index_uid: Option>, +} + +/// A type that tries to match either a star (*) or an IndexUid. +#[derive(Debug)] +enum StarOrIndexUid { + Star, + IndexUid(IndexUid), +} + +impl FromStr for StarOrIndexUid { + type Err = ::Err; + + fn from_str(s: &str) -> Result { + if s.trim() == "*" { + Ok(StarOrIndexUid::Star) + } else { + IndexUid::from_str(s).map(StarOrIndexUid::IndexUid) + } + } } #[rustfmt::skip] @@ -70,12 +90,29 @@ async fn get_tasks( let search_rules = &meilisearch.filters().search_rules; - // We first filter on potential indexes and make sure + // We first tranform a potential indexUid=* into a + // "not specified indexUid filter". + let index_uid = + match index_uid { + Some(indexes) => indexes + .into_inner() + .into_iter() + .fold(Some(Vec::new()), |acc, val| match (acc, val) { + (None, _) | (_, StarOrIndexUid::Star) => None, + (Some(mut acc), StarOrIndexUid::IndexUid(uid)) => { + acc.push(uid); + Some(acc) + } + }), + None => None, + }; + + // Then we filter on potential indexes and make sure // that the search filter restrictions are also applied. let indexes_filters = match index_uid { Some(indexes) => { let mut filters = TaskFilter::default(); - for name in indexes.into_inner() { + for name in indexes { if search_rules.is_index_authorized(&name) { filters.filter_index(name.to_string()); } diff --git a/meilisearch-http/tests/tasks/mod.rs b/meilisearch-http/tests/tasks/mod.rs index 80bf6cb3d..b14491fd2 100644 --- a/meilisearch-http/tests/tasks/mod.rs +++ b/meilisearch-http/tests/tasks/mod.rs @@ -59,6 +59,31 @@ async fn list_tasks() { assert_eq!(response["results"].as_array().unwrap().len(), 2); } +#[actix_rt::test] +async fn list_tasks_with_index_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.service.get("/tasks?indexUid=test").await; + assert_eq!(code, 200); + assert_eq!(response["results"].as_array().unwrap().len(), 2); + + let (response, code) = index.service.get("/tasks?indexUid=*").await; + assert_eq!(code, 200); + assert_eq!(response["results"].as_array().unwrap().len(), 2); + + let (response, code) = index.service.get("/tasks?indexUid=*,pasteque").await; + assert_eq!(code, 200); + assert_eq!(response["results"].as_array().unwrap().len(), 2); +} + #[actix_rt::test] async fn list_tasks_status_filtered() { let server = Server::new().await; From 082d6b89ffffe1d5e30a9ddadb7d2211dec56420 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Mon, 30 May 2022 17:01:51 +0200 Subject: [PATCH 09/11] Make the StarOrIndexUid Generic and call it StarOr --- meilisearch-http/src/routes/tasks.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/meilisearch-http/src/routes/tasks.rs b/meilisearch-http/src/routes/tasks.rs index 93af5af26..a5dcc6513 100644 --- a/meilisearch-http/src/routes/tasks.rs +++ b/meilisearch-http/src/routes/tasks.rs @@ -24,24 +24,25 @@ pub struct TaskFilterQuery { #[serde(rename = "type")] type_: Option>, status: Option>, - index_uid: Option>, + index_uid: Option>>, } -/// A type that tries to match either a star (*) or an IndexUid. +/// A type that tries to match either a star (*) or +/// any other thing that implements `FromStr`. #[derive(Debug)] -enum StarOrIndexUid { +enum StarOr { Star, - IndexUid(IndexUid), + Other(T), } -impl FromStr for StarOrIndexUid { - type Err = ::Err; +impl FromStr for StarOr { + type Err = T::Err; fn from_str(s: &str) -> Result { if s.trim() == "*" { - Ok(StarOrIndexUid::Star) + Ok(StarOr::Star) } else { - IndexUid::from_str(s).map(StarOrIndexUid::IndexUid) + T::from_str(s).map(StarOr::Other) } } } @@ -98,8 +99,8 @@ async fn get_tasks( .into_inner() .into_iter() .fold(Some(Vec::new()), |acc, val| match (acc, val) { - (None, _) | (_, StarOrIndexUid::Star) => None, - (Some(mut acc), StarOrIndexUid::IndexUid(uid)) => { + (None, _) | (_, StarOr::Star) => None, + (Some(mut acc), StarOr::Other(uid)) => { acc.push(uid); Some(acc) } From 8800b348f095891ef447a03df361982ea46ad199 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Mon, 30 May 2022 17:12:53 +0200 Subject: [PATCH 10/11] Implement the StarOr on all the tasks filters --- meilisearch-http/src/routes/tasks.rs | 49 +++++++++++++++------------- meilisearch-http/tests/tasks/mod.rs | 27 ++++++++++++++- 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/meilisearch-http/src/routes/tasks.rs b/meilisearch-http/src/routes/tasks.rs index a5dcc6513..6bb9d1e91 100644 --- a/meilisearch-http/src/routes/tasks.rs +++ b/meilisearch-http/src/routes/tasks.rs @@ -22,8 +22,8 @@ pub fn configure(cfg: &mut web::ServiceConfig) { #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct TaskFilterQuery { #[serde(rename = "type")] - type_: Option>, - status: Option>, + type_: Option>>, + status: Option>>, index_uid: Option>>, } @@ -47,6 +47,20 @@ impl FromStr for StarOr { } } +/// Extracts the raw values from the `StarOr` types and +/// return None if a `StarOr::Star` is encountered. +fn fold_star_or(content: Vec>) -> Option> { + content + .into_iter() + .fold(Some(Vec::new()), |acc, val| match (acc, val) { + (None, _) | (_, StarOr::Star) => None, + (Some(mut acc), StarOr::Other(uid)) => { + acc.push(uid); + Some(acc) + } + }) +} + #[rustfmt::skip] fn task_type_matches_content(type_: &TaskType, content: &TaskContent) -> bool { matches!((type_, content), @@ -91,25 +105,14 @@ async fn get_tasks( let search_rules = &meilisearch.filters().search_rules; - // We first tranform a potential indexUid=* into a - // "not specified indexUid filter". - let index_uid = - match index_uid { - Some(indexes) => indexes - .into_inner() - .into_iter() - .fold(Some(Vec::new()), |acc, val| match (acc, val) { - (None, _) | (_, StarOr::Star) => None, - (Some(mut acc), StarOr::Other(uid)) => { - acc.push(uid); - Some(acc) - } - }), - None => None, - }; + // We first tranform a potential indexUid=* into a "not specified indexUid filter" + // for every one of the filters: type, status, and indexUid. + let type_ = type_.map(CS::into_inner).and_then(fold_star_or); + let status = status.map(CS::into_inner).and_then(fold_star_or); + let index_uid = index_uid.map(CS::into_inner).and_then(fold_star_or); - // Then we filter on potential indexes and make sure - // that the search filter restrictions are also applied. + // Then we filter on potential indexes and make sure that the search filter + // restrictions are also applied. let indexes_filters = match index_uid { Some(indexes) => { let mut filters = TaskFilter::default(); @@ -135,7 +138,7 @@ async fn get_tasks( // Then we complete the task filter with other potential status and types filters. let filters = match (type_, status) { - (Some(CS(types)), Some(CS(statuses))) => { + (Some(types), Some(statuses)) => { let mut filters = indexes_filters.unwrap_or_default(); filters.filter_fn(move |task| { let matches_type = types @@ -148,7 +151,7 @@ async fn get_tasks( }); Some(filters) } - (Some(CS(types)), None) => { + (Some(types), None) => { let mut filters = indexes_filters.unwrap_or_default(); filters.filter_fn(move |task| { types @@ -157,7 +160,7 @@ async fn get_tasks( }); Some(filters) } - (None, Some(CS(statuses))) => { + (None, Some(statuses)) => { let mut filters = indexes_filters.unwrap_or_default(); filters.filter_fn(move |task| { statuses diff --git a/meilisearch-http/tests/tasks/mod.rs b/meilisearch-http/tests/tasks/mod.rs index b14491fd2..1ba7a4936 100644 --- a/meilisearch-http/tests/tasks/mod.rs +++ b/meilisearch-http/tests/tasks/mod.rs @@ -60,7 +60,7 @@ async fn list_tasks() { } #[actix_rt::test] -async fn list_tasks_with_index_filter() { +async fn list_tasks_with_star_filters() { let server = Server::new().await; let index = server.index("test"); index.create(None).await; @@ -82,6 +82,31 @@ async fn list_tasks_with_index_filter() { let (response, code) = index.service.get("/tasks?indexUid=*,pasteque").await; assert_eq!(code, 200); assert_eq!(response["results"].as_array().unwrap().len(), 2); + + let (response, code) = index.service.get("/tasks?type=*").await; + assert_eq!(code, 200); + assert_eq!(response["results"].as_array().unwrap().len(), 2); + + let (response, code) = index + .service + .get("/tasks?type=*,documentAdditionOrUpdate&status=*") + .await; + assert_eq!(code, 200, "{:?}", response); + assert_eq!(response["results"].as_array().unwrap().len(), 2); + + let (response, code) = index + .service + .get("/tasks?type=*,documentAdditionOrUpdate&status=*,failed&indexUid=test") + .await; + assert_eq!(code, 200, "{:?}", response); + assert_eq!(response["results"].as_array().unwrap().len(), 2); + + let (response, code) = index + .service + .get("/tasks?type=*,documentAdditionOrUpdate&status=*,failed&indexUid=test,*") + .await; + assert_eq!(code, 200, "{:?}", response); + assert_eq!(response["results"].as_array().unwrap().len(), 2); } #[actix_rt::test] From 1465b5e0ffaf2d80f7ab9ada11304a1dab76d40a Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Mon, 30 May 2022 17:38:25 +0200 Subject: [PATCH 11/11] Refactorize the tasks filters by moving the match inside --- meilisearch-http/src/routes/tasks.rs | 51 +++++++++++----------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/meilisearch-http/src/routes/tasks.rs b/meilisearch-http/src/routes/tasks.rs index 6bb9d1e91..1fe903abf 100644 --- a/meilisearch-http/src/routes/tasks.rs +++ b/meilisearch-http/src/routes/tasks.rs @@ -137,39 +137,28 @@ async fn get_tasks( }; // Then we complete the task filter with other potential status and types filters. - let filters = match (type_, status) { - (Some(types), Some(statuses)) => { - let mut filters = indexes_filters.unwrap_or_default(); - filters.filter_fn(move |task| { - let matches_type = types + let filters = if type_.is_some() || status.is_some() { + let mut filters = indexes_filters.unwrap_or_default(); + filters.filter_fn(move |task| { + let matches_type = match &type_ { + Some(types) => types .iter() - .any(|t| task_type_matches_content(t, &task.content)); - let matches_status = statuses + .any(|t| task_type_matches_content(t, &task.content)), + None => true, + }; + + let matches_status = match &status { + Some(statuses) => statuses .iter() - .any(|s| task_status_matches_events(s, &task.events)); - matches_type && matches_status - }); - Some(filters) - } - (Some(types), None) => { - let mut filters = indexes_filters.unwrap_or_default(); - filters.filter_fn(move |task| { - types - .iter() - .any(|t| task_type_matches_content(t, &task.content)) - }); - Some(filters) - } - (None, Some(statuses)) => { - let mut filters = indexes_filters.unwrap_or_default(); - filters.filter_fn(move |task| { - statuses - .iter() - .any(|s| task_status_matches_events(s, &task.events)) - }); - Some(filters) - } - (None, None) => indexes_filters, + .any(|t| task_status_matches_events(t, &task.events)), + None => true, + }; + + matches_type && matches_status + }); + Some(filters) + } else { + indexes_filters }; let tasks: TaskListView = meilisearch