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;