2021-12-02 23:03:26 +08:00
|
|
|
use actix_web::{web, HttpRequest, HttpResponse};
|
|
|
|
use meilisearch_error::ResponseError;
|
2022-05-25 18:05:24 +08:00
|
|
|
use meilisearch_lib::tasks::task::{TaskContent, TaskEvent, TaskId};
|
2021-12-06 22:45:41 +08:00
|
|
|
use meilisearch_lib::tasks::TaskFilter;
|
2022-05-17 22:08:23 +08:00
|
|
|
use meilisearch_lib::{IndexUid, MeiliSearch};
|
|
|
|
use serde::Deserialize;
|
|
|
|
use serde_cs::vec::CS;
|
2021-12-02 23:03:26 +08:00
|
|
|
use serde_json::json;
|
2022-05-30 19:59:27 +08:00
|
|
|
use std::str::FromStr;
|
2021-12-02 23:03:26 +08:00
|
|
|
|
|
|
|
use crate::analytics::Analytics;
|
|
|
|
use crate::extractors::authentication::{policies::*, GuardedData};
|
2022-03-05 03:12:44 +08:00
|
|
|
use crate::extractors::sequential_extractor::SeqHandler;
|
2022-05-17 22:08:23 +08:00
|
|
|
use crate::task::{TaskListView, TaskStatus, TaskType, TaskView};
|
2021-12-02 23:03:26 +08:00
|
|
|
|
|
|
|
pub fn configure(cfg: &mut web::ServiceConfig) {
|
2022-03-05 03:12:44 +08:00
|
|
|
cfg.service(web::resource("").route(web::get().to(SeqHandler(get_tasks))))
|
|
|
|
.service(web::resource("/{task_id}").route(web::get().to(SeqHandler(get_task))));
|
2021-12-02 23:03:26 +08:00
|
|
|
}
|
|
|
|
|
2022-05-17 22:08:23 +08:00
|
|
|
#[derive(Deserialize, Debug)]
|
|
|
|
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
2022-05-18 18:07:06 +08:00
|
|
|
pub struct TaskFilterQuery {
|
2022-05-17 22:08:23 +08:00
|
|
|
#[serde(rename = "type")]
|
|
|
|
type_: Option<CS<TaskType>>,
|
|
|
|
status: Option<CS<TaskStatus>>,
|
2022-05-30 19:59:27 +08:00
|
|
|
index_uid: Option<CS<StarOrIndexUid>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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 = <IndexUid as FromStr>::Err;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
if s.trim() == "*" {
|
|
|
|
Ok(StarOrIndexUid::Star)
|
|
|
|
} else {
|
|
|
|
IndexUid::from_str(s).map(StarOrIndexUid::IndexUid)
|
|
|
|
}
|
|
|
|
}
|
2022-05-17 22:08:23 +08:00
|
|
|
}
|
|
|
|
|
2022-05-18 18:07:06 +08:00
|
|
|
#[rustfmt::skip]
|
|
|
|
fn task_type_matches_content(type_: &TaskType, content: &TaskContent) -> bool {
|
|
|
|
matches!((type_, content),
|
2022-05-25 18:05:24 +08:00
|
|
|
(TaskType::IndexCreation, TaskContent::IndexCreation { .. })
|
2022-05-18 18:07:06 +08:00
|
|
|
| (TaskType::IndexUpdate, TaskContent::IndexUpdate { .. })
|
|
|
|
| (TaskType::IndexDeletion, TaskContent::IndexDeletion)
|
2022-05-25 18:05:24 +08:00
|
|
|
| (TaskType::DocumentAdditionOrUpdate, TaskContent::DocumentAddition { .. })
|
|
|
|
| (TaskType::DocumentDeletion, TaskContent::DocumentDeletion(_))
|
2022-05-18 18:07:06 +08:00
|
|
|
| (TaskType::SettingsUpdate, TaskContent::SettingsUpdate { .. })
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-05-25 18:05:24 +08:00
|
|
|
#[rustfmt::skip]
|
2022-05-18 18:07:06 +08:00
|
|
|
fn task_status_matches_events(status: &TaskStatus, events: &[TaskEvent]) -> bool {
|
|
|
|
events.last().map_or(false, |event| {
|
2022-05-25 18:05:24 +08:00
|
|
|
matches!((status, event),
|
|
|
|
(TaskStatus::Enqueued, TaskEvent::Created(_))
|
|
|
|
| (TaskStatus::Processing, TaskEvent::Processing(_) | TaskEvent::Batched { .. })
|
|
|
|
| (TaskStatus::Succeeded, TaskEvent::Succeded { .. })
|
|
|
|
| (TaskStatus::Failed, TaskEvent::Failed { .. }),
|
2022-05-18 18:07:06 +08:00
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-12-02 23:03:26 +08:00
|
|
|
async fn get_tasks(
|
2021-11-09 01:31:27 +08:00
|
|
|
meilisearch: GuardedData<ActionPolicy<{ actions::TASKS_GET }>, MeiliSearch>,
|
2022-05-18 18:07:06 +08:00
|
|
|
params: web::Query<TaskFilterQuery>,
|
2021-12-02 23:03:26 +08:00
|
|
|
req: HttpRequest,
|
|
|
|
analytics: web::Data<dyn Analytics>,
|
|
|
|
) -> Result<HttpResponse, ResponseError> {
|
|
|
|
analytics.publish(
|
|
|
|
"Tasks Seen".to_string(),
|
|
|
|
json!({ "per_task_uid": false }),
|
|
|
|
Some(&req),
|
|
|
|
);
|
|
|
|
|
2022-05-18 18:07:06 +08:00
|
|
|
let TaskFilterQuery {
|
2022-05-17 22:08:23 +08:00
|
|
|
type_,
|
|
|
|
status,
|
|
|
|
index_uid,
|
|
|
|
} = params.into_inner();
|
|
|
|
|
2022-01-12 22:35:33 +08:00
|
|
|
let search_rules = &meilisearch.filters().search_rules;
|
2022-05-18 18:07:06 +08:00
|
|
|
|
2022-05-30 19:59:27 +08:00
|
|
|
// 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
|
2022-05-18 18:07:06 +08:00
|
|
|
// that the search filter restrictions are also applied.
|
|
|
|
let indexes_filters = match index_uid {
|
2022-05-17 22:08:23 +08:00
|
|
|
Some(indexes) => {
|
|
|
|
let mut filters = TaskFilter::default();
|
2022-05-30 19:59:27 +08:00
|
|
|
for name in indexes {
|
2022-05-17 22:08:23 +08:00
|
|
|
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)
|
|
|
|
}
|
2021-12-06 22:45:41 +08:00
|
|
|
}
|
2022-01-12 22:35:33 +08:00
|
|
|
};
|
2021-12-06 22:45:41 +08:00
|
|
|
|
2022-05-18 18:07:06 +08:00
|
|
|
// 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()
|
2022-05-26 00:21:50 +08:00
|
|
|
.any(|t| task_type_matches_content(t, &task.content));
|
2022-05-18 18:07:06 +08:00
|
|
|
let matches_status = statuses
|
|
|
|
.iter()
|
2022-05-26 00:21:50 +08:00
|
|
|
.any(|s| task_status_matches_events(s, &task.events));
|
2022-05-18 18:07:06 +08:00
|
|
|
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()
|
2022-05-26 00:21:50 +08:00
|
|
|
.any(|t| task_type_matches_content(t, &task.content))
|
2022-05-18 18:07:06 +08:00
|
|
|
});
|
|
|
|
Some(filters)
|
|
|
|
}
|
|
|
|
(None, Some(CS(statuses))) => {
|
|
|
|
let mut filters = indexes_filters.unwrap_or_default();
|
|
|
|
filters.filter_fn(move |task| {
|
|
|
|
statuses
|
|
|
|
.iter()
|
2022-05-26 00:21:50 +08:00
|
|
|
.any(|s| task_status_matches_events(s, &task.events))
|
2022-05-18 18:07:06 +08:00
|
|
|
});
|
|
|
|
Some(filters)
|
|
|
|
}
|
|
|
|
(None, None) => indexes_filters,
|
|
|
|
};
|
|
|
|
|
2021-12-02 23:03:26 +08:00
|
|
|
let tasks: TaskListView = meilisearch
|
2021-12-06 22:45:41 +08:00
|
|
|
.list_tasks(filters, None, None)
|
2021-12-02 23:03:26 +08:00
|
|
|
.await?
|
|
|
|
.into_iter()
|
|
|
|
.map(TaskView::from)
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.into();
|
|
|
|
|
|
|
|
Ok(HttpResponse::Ok().json(tasks))
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn get_task(
|
2021-11-09 01:31:27 +08:00
|
|
|
meilisearch: GuardedData<ActionPolicy<{ actions::TASKS_GET }>, MeiliSearch>,
|
2021-12-02 23:03:26 +08:00
|
|
|
task_id: web::Path<TaskId>,
|
|
|
|
req: HttpRequest,
|
|
|
|
analytics: web::Data<dyn Analytics>,
|
|
|
|
) -> Result<HttpResponse, ResponseError> {
|
|
|
|
analytics.publish(
|
|
|
|
"Tasks Seen".to_string(),
|
|
|
|
json!({ "per_task_uid": true }),
|
|
|
|
Some(&req),
|
|
|
|
);
|
|
|
|
|
2022-01-12 22:35:33 +08:00
|
|
|
let search_rules = &meilisearch.filters().search_rules;
|
|
|
|
let filters = if search_rules.is_index_authorized("*") {
|
|
|
|
None
|
|
|
|
} else {
|
2021-12-06 22:45:41 +08:00
|
|
|
let mut filters = TaskFilter::default();
|
2022-01-12 22:35:33 +08:00
|
|
|
for (index, _policy) in search_rules.clone() {
|
|
|
|
filters.filter_index(index);
|
2021-12-06 22:45:41 +08:00
|
|
|
}
|
2022-01-12 22:35:33 +08:00
|
|
|
Some(filters)
|
|
|
|
};
|
2021-12-06 22:45:41 +08:00
|
|
|
|
2021-12-02 23:03:26 +08:00
|
|
|
let task: TaskView = meilisearch
|
2021-12-06 22:45:41 +08:00
|
|
|
.get_task(task_id.into_inner(), filters)
|
2021-12-02 23:03:26 +08:00
|
|
|
.await?
|
|
|
|
.into();
|
|
|
|
|
|
|
|
Ok(HttpResponse::Ok().json(task))
|
|
|
|
}
|