2022-09-27 16:33:37 +02:00
use actix_web ::web ::Data ;
2021-12-02 16:03:26 +01:00
use actix_web ::{ web , HttpRequest , HttpResponse } ;
2023-01-11 20:33:07 +01:00
use deserr ::DeserializeFromValue ;
2022-10-13 11:09:00 +02:00
use index_scheduler ::{ IndexScheduler , Query , TaskId } ;
2023-01-16 16:59:26 +01:00
use meilisearch_types ::deserr ::query_params ::Param ;
use meilisearch_types ::deserr ::DeserrQueryParamError ;
2023-01-17 11:05:01 +01:00
use meilisearch_types ::error ::deserr_codes ::* ;
use meilisearch_types ::error ::{ InvalidTaskDateError , ResponseError } ;
2022-06-06 12:38:46 +02:00
use meilisearch_types ::index_uid ::IndexUid ;
2022-10-12 03:21:25 +02:00
use meilisearch_types ::settings ::{ Settings , Unchecked } ;
2023-01-16 16:59:26 +01:00
use meilisearch_types ::star_or ::{ OptionStarOr , OptionStarOrList } ;
2022-10-26 12:57:29 +02:00
use meilisearch_types ::tasks ::{
serialize_duration , Details , IndexSwap , Kind , KindWithContent , Status , Task ,
} ;
2023-01-16 16:59:26 +01:00
use serde ::Serialize ;
2021-12-02 16:03:26 +01:00
use serde_json ::json ;
2023-01-11 20:33:07 +01:00
use time ::format_description ::well_known ::Rfc3339 ;
use time ::macros ::format_description ;
use time ::{ Date , Duration , OffsetDateTime , Time } ;
2022-10-26 11:11:53 +02:00
use tokio ::task ;
2021-12-02 16:03:26 +01:00
2023-01-12 13:55:53 +01:00
use super ::SummarizedTaskView ;
2021-12-02 16:03:26 +01:00
use crate ::analytics ::Analytics ;
2022-10-20 18:00:07 +02:00
use crate ::extractors ::authentication ::policies ::* ;
use crate ::extractors ::authentication ::GuardedData ;
2023-01-11 20:33:07 +01:00
use crate ::extractors ::query_parameters ::QueryParameter ;
2022-03-04 20:12:44 +01:00
use crate ::extractors ::sequential_extractor ::SeqHandler ;
2021-12-02 16:03:26 +01:00
2023-01-16 16:59:26 +01:00
const DEFAULT_LIMIT : u32 = 20 ;
2022-06-01 12:04:01 +02:00
2021-12-02 16:03:26 +01:00
pub fn configure ( cfg : & mut web ::ServiceConfig ) {
2022-10-13 12:48:23 +02:00
cfg . service (
web ::resource ( " " )
. route ( web ::get ( ) . to ( SeqHandler ( get_tasks ) ) )
. route ( web ::delete ( ) . to ( SeqHandler ( delete_tasks ) ) ) ,
)
2022-10-26 11:11:53 +02:00
. service ( web ::resource ( " /cancel " ) . route ( web ::post ( ) . to ( SeqHandler ( cancel_tasks ) ) ) )
. service ( web ::resource ( " /{task_id} " ) . route ( web ::get ( ) . to ( SeqHandler ( get_task ) ) ) ) ;
2021-12-02 16:03:26 +01:00
}
2022-10-26 15:14:46 +02:00
#[ derive(Debug, Clone, PartialEq, Eq, Serialize) ]
2022-10-12 03:21:25 +02:00
#[ serde(rename_all = " camelCase " ) ]
pub struct TaskView {
pub uid : TaskId ,
#[ serde(default) ]
2022-10-26 13:39:58 +02:00
pub index_uid : Option < String > ,
2022-10-12 03:21:25 +02:00
pub status : Status ,
#[ serde(rename = " type " ) ]
pub kind : Kind ,
2022-10-18 13:57:58 +02:00
pub canceled_by : Option < TaskId > ,
2022-10-12 03:21:25 +02:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub details : Option < DetailsView > ,
pub error : Option < ResponseError > ,
2022-10-23 11:23:24 +02:00
#[ serde(serialize_with = " serialize_duration " , default) ]
2022-10-12 03:21:25 +02:00
pub duration : Option < Duration > ,
#[ serde(with = " time::serde::rfc3339 " ) ]
pub enqueued_at : OffsetDateTime ,
2022-10-23 11:23:24 +02:00
#[ serde(with = " time::serde::rfc3339::option " , default) ]
2022-10-12 03:21:25 +02:00
pub started_at : Option < OffsetDateTime > ,
2022-10-23 11:23:24 +02:00
#[ serde(with = " time::serde::rfc3339::option " , default) ]
2022-10-12 03:21:25 +02:00
pub finished_at : Option < OffsetDateTime > ,
}
2022-10-13 12:48:23 +02:00
impl TaskView {
2022-10-17 16:30:18 +02:00
pub fn from_task ( task : & Task ) -> TaskView {
2022-10-12 03:21:25 +02:00
TaskView {
uid : task . uid ,
2022-10-26 13:39:58 +02:00
index_uid : task . index_uid ( ) . map ( ToOwned ::to_owned ) ,
2022-10-12 03:21:25 +02:00
status : task . status ,
kind : task . kind . as_kind ( ) ,
2022-10-18 13:57:58 +02:00
canceled_by : task . canceled_by ,
2022-10-13 12:48:23 +02:00
details : task . details . clone ( ) . map ( DetailsView ::from ) ,
2022-10-12 03:21:25 +02:00
error : task . error . clone ( ) ,
2022-10-20 18:00:07 +02:00
duration : task . started_at . zip ( task . finished_at ) . map ( | ( start , end ) | end - start ) ,
2022-10-12 03:21:25 +02:00
enqueued_at : task . enqueued_at ,
started_at : task . started_at ,
finished_at : task . finished_at ,
}
}
}
2022-10-26 15:14:46 +02:00
#[ derive(Default, Debug, PartialEq, Eq, Clone, Serialize) ]
2022-10-12 03:21:25 +02:00
#[ serde(rename_all = " camelCase " ) ]
pub struct DetailsView {
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub received_documents : Option < u64 > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2022-11-28 16:27:41 +01:00
pub indexed_documents : Option < Option < u64 > > ,
2022-10-12 03:21:25 +02:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub primary_key : Option < Option < String > > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2022-11-28 16:27:41 +01:00
pub provided_ids : Option < usize > ,
2022-10-12 03:21:25 +02:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
pub deleted_documents : Option < Option < u64 > > ,
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2022-10-17 20:07:53 +02:00
pub matched_tasks : Option < u64 > ,
2022-10-12 03:21:25 +02:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2022-10-19 11:09:40 +02:00
pub canceled_tasks : Option < Option < u64 > > ,
2022-10-18 13:47:22 +02:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2022-10-19 11:09:40 +02:00
pub deleted_tasks : Option < Option < u64 > > ,
2022-10-12 03:21:25 +02:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2022-11-28 16:27:41 +01:00
pub original_filter : Option < String > ,
2022-10-12 03:21:25 +02:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2022-11-28 16:27:41 +01:00
pub dump_uid : Option < Option < String > > ,
2022-10-12 03:21:25 +02:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
#[ serde(flatten) ]
2022-10-22 16:35:42 +02:00
pub settings : Option < Box < Settings < Unchecked > > > ,
2022-10-17 16:30:18 +02:00
#[ serde(skip_serializing_if = " Option::is_none " ) ]
2022-10-26 12:57:29 +02:00
pub swaps : Option < Vec < IndexSwap > > ,
2022-10-12 03:21:25 +02:00
}
impl From < Details > for DetailsView {
fn from ( details : Details ) -> Self {
2022-10-22 16:35:42 +02:00
match details {
2022-10-21 18:03:10 +02:00
Details ::DocumentAdditionOrUpdate { received_documents , indexed_documents } = > {
DetailsView {
received_documents : Some ( received_documents ) ,
2022-11-28 16:27:41 +01:00
indexed_documents : Some ( indexed_documents ) ,
2022-10-21 18:03:10 +02:00
.. DetailsView ::default ( )
}
}
Details ::SettingsUpdate { settings } = > {
2022-10-20 18:00:07 +02:00
DetailsView { settings : Some ( settings ) , .. DetailsView ::default ( ) }
}
Details ::IndexInfo { primary_key } = > {
DetailsView { primary_key : Some ( primary_key ) , .. DetailsView ::default ( ) }
}
2022-10-25 11:02:26 +02:00
Details ::DocumentDeletion {
2022-11-28 16:27:41 +01:00
provided_ids : received_document_ids ,
2022-10-25 11:02:26 +02:00
deleted_documents ,
} = > DetailsView {
2022-11-28 16:27:41 +01:00
provided_ids : Some ( received_document_ids ) ,
2022-10-12 03:21:25 +02:00
deleted_documents : Some ( deleted_documents ) ,
.. DetailsView ::default ( )
} ,
2022-10-20 18:00:07 +02:00
Details ::ClearAll { deleted_documents } = > {
DetailsView { deleted_documents : Some ( deleted_documents ) , .. DetailsView ::default ( ) }
}
2022-11-28 16:27:41 +01:00
Details ::TaskCancelation { matched_tasks , canceled_tasks , original_filter } = > {
2022-10-20 18:00:07 +02:00
DetailsView {
matched_tasks : Some ( matched_tasks ) ,
canceled_tasks : Some ( canceled_tasks ) ,
2022-11-28 16:27:41 +01:00
original_filter : Some ( original_filter ) ,
.. DetailsView ::default ( )
}
}
Details ::TaskDeletion { matched_tasks , deleted_tasks , original_filter } = > {
DetailsView {
matched_tasks : Some ( matched_tasks ) ,
deleted_tasks : Some ( deleted_tasks ) ,
original_filter : Some ( original_filter ) ,
2022-10-20 18:00:07 +02:00
.. DetailsView ::default ( )
}
}
Details ::Dump { dump_uid } = > {
DetailsView { dump_uid : Some ( dump_uid ) , .. DetailsView ::default ( ) }
}
Details ::IndexSwap { swaps } = > {
2022-10-26 12:57:29 +02:00
DetailsView { swaps : Some ( swaps ) , .. Default ::default ( ) }
2022-10-20 18:00:07 +02:00
}
2022-10-12 03:21:25 +02:00
}
}
}
2023-01-11 20:33:07 +01:00
#[ derive(Debug, DeserializeFromValue) ]
2023-01-12 13:55:53 +01:00
#[ deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields) ]
2022-07-07 10:56:02 +02:00
pub struct TasksFilterQuery {
2023-01-16 16:59:26 +01:00
#[ deserr(default = Param(DEFAULT_LIMIT), error = DeserrQueryParamError<InvalidTaskLimit>) ]
pub limit : Param < u32 > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskFrom>) ]
pub from : Option < Param < TaskId > > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskUids>) ]
pub uids : OptionStarOrList < u32 > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskCanceledBy>) ]
pub canceled_by : OptionStarOrList < u32 > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskTypes>) ]
pub types : OptionStarOrList < Kind > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskStatuses>) ]
pub statuses : OptionStarOrList < Status > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidIndexUid>) ]
pub index_uids : OptionStarOrList < IndexUid > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskAfterEnqueuedAt>, from(OptionStarOr<String>) = deserialize_date_after -> InvalidTaskDateError) ]
pub after_enqueued_at : OptionStarOr < OffsetDateTime > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskBeforeEnqueuedAt>, from(OptionStarOr<String>) = deserialize_date_before -> InvalidTaskDateError) ]
pub before_enqueued_at : OptionStarOr < OffsetDateTime > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskAfterStartedAt>, from(OptionStarOr<String>) = deserialize_date_after -> InvalidTaskDateError) ]
pub after_started_at : OptionStarOr < OffsetDateTime > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskBeforeStartedAt>, from(OptionStarOr<String>) = deserialize_date_before -> InvalidTaskDateError) ]
pub before_started_at : OptionStarOr < OffsetDateTime > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskAfterFinishedAt>, from(OptionStarOr<String>) = deserialize_date_after -> InvalidTaskDateError) ]
pub after_finished_at : OptionStarOr < OffsetDateTime > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskBeforeFinishedAt>, from(OptionStarOr<String>) = deserialize_date_before -> InvalidTaskDateError) ]
pub before_finished_at : OptionStarOr < OffsetDateTime > ,
}
impl TasksFilterQuery {
fn into_query ( self ) -> Query {
Query {
limit : Some ( self . limit . 0 ) ,
from : self . from . as_deref ( ) . copied ( ) ,
statuses : self . statuses . merge_star_and_none ( ) ,
types : self . types . merge_star_and_none ( ) ,
index_uids : self . index_uids . map ( | x | x . to_string ( ) ) . merge_star_and_none ( ) ,
uids : self . uids . merge_star_and_none ( ) ,
canceled_by : self . canceled_by . merge_star_and_none ( ) ,
before_enqueued_at : self . before_enqueued_at . merge_star_and_none ( ) ,
after_enqueued_at : self . after_enqueued_at . merge_star_and_none ( ) ,
before_started_at : self . before_started_at . merge_star_and_none ( ) ,
after_started_at : self . after_started_at . merge_star_and_none ( ) ,
before_finished_at : self . before_finished_at . merge_star_and_none ( ) ,
after_finished_at : self . after_finished_at . merge_star_and_none ( ) ,
}
}
}
impl TaskDeletionOrCancelationQuery {
fn is_empty ( & self ) -> bool {
matches! (
self ,
TaskDeletionOrCancelationQuery {
uids : OptionStarOrList ::None ,
canceled_by : OptionStarOrList ::None ,
types : OptionStarOrList ::None ,
statuses : OptionStarOrList ::None ,
index_uids : OptionStarOrList ::None ,
after_enqueued_at : OptionStarOr ::None ,
before_enqueued_at : OptionStarOr ::None ,
after_started_at : OptionStarOr ::None ,
before_started_at : OptionStarOr ::None ,
after_finished_at : OptionStarOr ::None ,
before_finished_at : OptionStarOr ::None
}
)
}
2022-05-18 12:07:06 +02:00
}
2023-01-16 16:59:26 +01:00
#[ derive(Debug, DeserializeFromValue) ]
2023-01-12 13:55:53 +01:00
#[ deserr(error = DeserrQueryParamError, rename_all = camelCase, deny_unknown_fields) ]
2022-11-28 16:27:41 +01:00
pub struct TaskDeletionOrCancelationQuery {
2023-01-16 16:59:26 +01:00
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskUids>) ]
pub uids : OptionStarOrList < u32 > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskCanceledBy>) ]
pub canceled_by : OptionStarOrList < u32 > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskTypes>) ]
pub types : OptionStarOrList < Kind > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskStatuses>) ]
pub statuses : OptionStarOrList < Status > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidIndexUid>) ]
pub index_uids : OptionStarOrList < IndexUid > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskAfterEnqueuedAt>, from(OptionStarOr<String>) = deserialize_date_after -> InvalidTaskDateError) ]
pub after_enqueued_at : OptionStarOr < OffsetDateTime > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskBeforeEnqueuedAt>, from(OptionStarOr<String>) = deserialize_date_before -> InvalidTaskDateError) ]
pub before_enqueued_at : OptionStarOr < OffsetDateTime > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskAfterStartedAt>, from(OptionStarOr<String>) = deserialize_date_after -> InvalidTaskDateError) ]
pub after_started_at : OptionStarOr < OffsetDateTime > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskBeforeStartedAt>, from(OptionStarOr<String>) = deserialize_date_before -> InvalidTaskDateError) ]
pub before_started_at : OptionStarOr < OffsetDateTime > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskAfterFinishedAt>, from(OptionStarOr<String>) = deserialize_date_after -> InvalidTaskDateError) ]
pub after_finished_at : OptionStarOr < OffsetDateTime > ,
#[ deserr(default, error = DeserrQueryParamError<InvalidTaskBeforeFinishedAt>, from(OptionStarOr<String>) = deserialize_date_before -> InvalidTaskDateError) ]
pub before_finished_at : OptionStarOr < OffsetDateTime > ,
}
impl TaskDeletionOrCancelationQuery {
fn into_query ( self ) -> Query {
Query {
limit : None ,
from : None ,
statuses : self . statuses . merge_star_and_none ( ) ,
types : self . types . merge_star_and_none ( ) ,
index_uids : self . index_uids . map ( | x | x . to_string ( ) ) . merge_star_and_none ( ) ,
uids : self . uids . merge_star_and_none ( ) ,
canceled_by : self . canceled_by . merge_star_and_none ( ) ,
before_enqueued_at : self . before_enqueued_at . merge_star_and_none ( ) ,
after_enqueued_at : self . after_enqueued_at . merge_star_and_none ( ) ,
before_started_at : self . before_started_at . merge_star_and_none ( ) ,
after_started_at : self . after_started_at . merge_star_and_none ( ) ,
before_finished_at : self . before_finished_at . merge_star_and_none ( ) ,
after_finished_at : self . after_finished_at . merge_star_and_none ( ) ,
}
}
2022-10-18 14:48:40 +02:00
}
async fn cancel_tasks (
index_scheduler : GuardedData < ActionPolicy < { actions ::TASKS_CANCEL } > , Data < IndexScheduler > > ,
2023-01-12 13:55:53 +01:00
params : QueryParameter < TaskDeletionOrCancelationQuery , DeserrQueryParamError > ,
2022-10-18 17:47:47 +02:00
req : HttpRequest ,
2022-11-28 16:27:41 +01:00
analytics : web ::Data < dyn Analytics > ,
2022-10-18 14:48:40 +02:00
) -> Result < HttpResponse , ResponseError > {
2023-01-16 16:59:26 +01:00
let params = params . into_inner ( ) ;
if params . is_empty ( ) {
return Err ( index_scheduler ::Error ::TaskCancelationWithEmptyQuery . into ( ) ) ;
}
2022-10-18 14:48:40 +02:00
2022-11-28 16:27:41 +01:00
analytics . publish (
" Tasks Canceled " . to_string ( ) ,
json! ( {
2023-01-16 16:59:26 +01:00
" filtered_by_uid " : params . uids . is_some ( ) ,
" filtered_by_index_uid " : params . index_uids . is_some ( ) ,
" filtered_by_type " : params . types . is_some ( ) ,
" filtered_by_status " : params . statuses . is_some ( ) ,
" filtered_by_canceled_by " : params . canceled_by . is_some ( ) ,
" filtered_by_before_enqueued_at " : params . before_enqueued_at . is_some ( ) ,
" filtered_by_after_enqueued_at " : params . after_enqueued_at . is_some ( ) ,
" filtered_by_before_started_at " : params . before_started_at . is_some ( ) ,
" filtered_by_after_started_at " : params . after_started_at . is_some ( ) ,
" filtered_by_before_finished_at " : params . before_finished_at . is_some ( ) ,
" filtered_by_after_finished_at " : params . after_finished_at . is_some ( ) ,
2022-11-28 16:27:41 +01:00
} ) ,
Some ( & req ) ,
) ;
2022-10-18 14:48:40 +02:00
2023-01-16 16:59:26 +01:00
let query = params . into_query ( ) ;
2022-10-18 14:48:40 +02:00
2022-10-27 11:17:50 +02:00
let tasks = index_scheduler . get_task_ids_from_authorized_indexes (
& index_scheduler . read_txn ( ) ? ,
& query ,
& index_scheduler . filters ( ) . search_rules . authorized_indexes ( ) ,
) ? ;
2022-10-20 18:00:07 +02:00
let task_cancelation =
2022-11-28 16:27:41 +01:00
KindWithContent ::TaskCancelation { query : format ! ( " ?{} " , req . query_string ( ) ) , tasks } ;
2022-10-18 14:48:40 +02:00
2022-10-26 11:11:53 +02:00
let task = task ::spawn_blocking ( move | | index_scheduler . register ( task_cancelation ) ) . await ? ? ;
2022-10-27 01:00:56 +02:00
let task : SummarizedTaskView = task . into ( ) ;
2022-10-18 14:48:40 +02:00
2022-10-27 01:00:56 +02:00
Ok ( HttpResponse ::Ok ( ) . json ( task ) )
2022-10-18 14:48:40 +02:00
}
2022-10-13 11:09:00 +02:00
async fn delete_tasks (
index_scheduler : GuardedData < ActionPolicy < { actions ::TASKS_DELETE } > , Data < IndexScheduler > > ,
2023-01-12 13:55:53 +01:00
params : QueryParameter < TaskDeletionOrCancelationQuery , DeserrQueryParamError > ,
2022-10-18 17:47:47 +02:00
req : HttpRequest ,
2022-11-28 16:27:41 +01:00
analytics : web ::Data < dyn Analytics > ,
2022-10-13 11:09:00 +02:00
) -> Result < HttpResponse , ResponseError > {
2023-01-16 16:59:26 +01:00
let params = params . into_inner ( ) ;
if params . is_empty ( ) {
return Err ( index_scheduler ::Error ::TaskDeletionWithEmptyQuery . into ( ) ) ;
}
2022-10-13 12:48:23 +02:00
2022-11-28 16:27:41 +01:00
analytics . publish (
" Tasks Deleted " . to_string ( ) ,
json! ( {
2023-01-16 16:59:26 +01:00
" filtered_by_uid " : params . uids . is_some ( ) ,
" filtered_by_index_uid " : params . index_uids . is_some ( ) ,
" filtered_by_type " : params . types . is_some ( ) ,
" filtered_by_status " : params . statuses . is_some ( ) ,
" filtered_by_canceled_by " : params . canceled_by . is_some ( ) ,
" filtered_by_before_enqueued_at " : params . before_enqueued_at . is_some ( ) ,
" filtered_by_after_enqueued_at " : params . after_enqueued_at . is_some ( ) ,
" filtered_by_before_started_at " : params . before_started_at . is_some ( ) ,
" filtered_by_after_started_at " : params . after_started_at . is_some ( ) ,
" filtered_by_before_finished_at " : params . before_finished_at . is_some ( ) ,
" filtered_by_after_finished_at " : params . after_finished_at . is_some ( ) ,
2022-11-28 16:27:41 +01:00
} ) ,
Some ( & req ) ,
) ;
2023-01-16 16:59:26 +01:00
let query = params . into_query ( ) ;
2022-10-13 12:48:23 +02:00
2022-10-27 11:17:50 +02:00
let tasks = index_scheduler . get_task_ids_from_authorized_indexes (
& index_scheduler . read_txn ( ) ? ,
& query ,
& index_scheduler . filters ( ) . search_rules . authorized_indexes ( ) ,
) ? ;
2022-10-20 18:00:07 +02:00
let task_deletion =
2022-11-28 16:27:41 +01:00
KindWithContent ::TaskDeletion { query : format ! ( " ?{} " , req . query_string ( ) ) , tasks } ;
2022-10-15 11:03:24 +02:00
2022-10-26 11:11:53 +02:00
let task = task ::spawn_blocking ( move | | index_scheduler . register ( task_deletion ) ) . await ? ? ;
2022-10-27 01:00:56 +02:00
let task : SummarizedTaskView = task . into ( ) ;
2022-10-13 11:09:00 +02:00
2022-10-27 01:00:56 +02:00
Ok ( HttpResponse ::Ok ( ) . json ( task ) )
2022-10-13 11:09:00 +02:00
}
2022-10-23 11:23:24 +02:00
#[ derive(Debug, Serialize) ]
pub struct AllTasks {
results : Vec < TaskView > ,
limit : u32 ,
from : Option < u32 > ,
next : Option < u32 > ,
}
2021-12-02 16:03:26 +01:00
async fn get_tasks (
2022-09-27 16:33:37 +02:00
index_scheduler : GuardedData < ActionPolicy < { actions ::TASKS_GET } > , Data < IndexScheduler > > ,
2023-01-12 13:55:53 +01:00
params : QueryParameter < TasksFilterQuery , DeserrQueryParamError > ,
2021-12-02 16:03:26 +01:00
req : HttpRequest ,
analytics : web ::Data < dyn Analytics > ,
) -> Result < HttpResponse , ResponseError > {
2023-01-16 16:59:26 +01:00
let mut params = params . into_inner ( ) ;
2022-11-28 16:27:41 +01:00
analytics . get_tasks ( & params , & req ) ;
2022-05-31 11:56:51 +02:00
// We +1 just to know if there is more after this "page" or not.
2023-01-16 16:59:26 +01:00
params . limit . 0 = params . limit . 0. saturating_add ( 1 ) ;
let limit = params . limit . 0 ;
let query = params . into_query ( ) ;
2022-05-31 11:56:51 +02:00
2022-10-27 11:17:50 +02:00
let mut tasks_results : Vec < TaskView > = index_scheduler
. get_tasks_from_authorized_indexes (
query ,
index_scheduler . filters ( ) . search_rules . authorized_indexes ( ) ,
) ?
. into_iter ( )
. map ( | t | TaskView ::from_task ( & t ) )
. collect ( ) ;
2022-05-31 11:56:51 +02:00
// If we were able to fetch the number +1 tasks we asked
// it means that there is more to come.
2022-09-27 16:33:37 +02:00
let next = if tasks_results . len ( ) = = limit as usize {
2022-06-01 15:30:39 +02:00
tasks_results . pop ( ) . map ( | t | t . uid )
2022-05-31 11:56:51 +02:00
} else {
None
} ;
2022-06-01 15:30:39 +02:00
let from = tasks_results . first ( ) . map ( | t | t . uid ) ;
2022-10-23 11:23:24 +02:00
let tasks = AllTasks { results : tasks_results , limit : limit . saturating_sub ( 1 ) , from , next } ;
2021-12-02 16:03:26 +01:00
Ok ( HttpResponse ::Ok ( ) . json ( tasks ) )
}
async fn get_task (
2022-09-27 16:33:37 +02:00
index_scheduler : GuardedData < ActionPolicy < { actions ::TASKS_GET } > , Data < IndexScheduler > > ,
2022-11-28 16:27:41 +01:00
task_uid : web ::Path < String > ,
2021-12-02 16:03:26 +01:00
req : HttpRequest ,
analytics : web ::Data < dyn Analytics > ,
) -> Result < HttpResponse , ResponseError > {
2022-11-28 16:27:41 +01:00
let task_uid_string = task_uid . into_inner ( ) ;
let task_uid : TaskId = match task_uid_string . parse ( ) {
Ok ( id ) = > id ,
Err ( _e ) = > {
return Err ( index_scheduler ::Error ::InvalidTaskUids { task_uid : task_uid_string } . into ( ) )
}
} ;
2022-09-22 20:02:55 +02:00
2022-10-20 18:00:07 +02:00
analytics . publish ( " Tasks Seen " . to_string ( ) , json! ( { " per_task_uid " : true } ) , Some ( & req ) ) ;
2021-12-02 16:03:26 +01:00
2022-11-28 16:27:41 +01:00
let query = index_scheduler ::Query { uids : Some ( vec! [ task_uid ] ) , .. Query ::default ( ) } ;
2022-09-22 12:14:51 +02:00
2022-10-27 11:17:50 +02:00
if let Some ( task ) = index_scheduler
. get_tasks_from_authorized_indexes (
query ,
index_scheduler . filters ( ) . search_rules . authorized_indexes ( ) ,
) ?
. first ( )
{
2022-10-22 16:35:42 +02:00
let task_view = TaskView ::from_task ( task ) ;
2022-10-13 12:48:23 +02:00
Ok ( HttpResponse ::Ok ( ) . json ( task_view ) )
2022-09-22 20:02:55 +02:00
} else {
2022-11-28 16:27:41 +01:00
Err ( index_scheduler ::Error ::TaskNotFound ( task_uid ) . into ( ) )
2022-09-22 20:02:55 +02:00
}
2021-12-02 16:03:26 +01:00
}
2022-10-13 11:09:00 +02:00
2023-01-11 20:33:07 +01:00
pub enum DeserializeDateOption {
Before ,
After ,
}
2022-10-19 12:59:12 +02:00
2023-01-11 20:33:07 +01:00
pub fn deserialize_date (
value : & str ,
option : DeserializeDateOption ,
2023-01-16 16:59:26 +01:00
) -> std ::result ::Result < OffsetDateTime , InvalidTaskDateError > {
2023-01-11 20:33:07 +01:00
// We can't parse using time's rfc3339 format, since then we won't know what part of the
// datetime was not explicitly specified, and thus we won't be able to increment it to the
// next step.
if let Ok ( datetime ) = OffsetDateTime ::parse ( value , & Rfc3339 ) {
// fully specified up to the second
// we assume that the subseconds are 0 if not specified, and we don't increment to the next second
Ok ( datetime )
} else if let Ok ( datetime ) = Date ::parse (
value ,
format_description! ( " [year repr:full base:calendar]-[month repr:numerical]-[day] " ) ,
) {
let datetime = datetime . with_time ( Time ::MIDNIGHT ) . assume_utc ( ) ;
// add one day since the time was not specified
match option {
DeserializeDateOption ::Before = > Ok ( datetime ) ,
DeserializeDateOption ::After = > {
let datetime = datetime . checked_add ( Duration ::days ( 1 ) ) . unwrap_or ( datetime ) ;
Ok ( datetime )
2022-10-19 16:07:04 +02:00
}
2022-10-19 12:59:12 +02:00
}
2023-01-11 20:33:07 +01:00
} else {
2023-01-16 16:59:26 +01:00
Err ( InvalidTaskDateError ( value . to_owned ( ) ) )
2023-01-11 20:33:07 +01:00
}
}
pub fn deserialize_date_after (
2023-01-16 16:59:26 +01:00
value : OptionStarOr < String > ,
) -> std ::result ::Result < OptionStarOr < OffsetDateTime > , InvalidTaskDateError > {
value . try_map ( | x | deserialize_date ( & x , DeserializeDateOption ::After ) )
2022-10-19 12:59:12 +02:00
}
2023-01-16 16:59:26 +01:00
pub fn deserialize_date_before (
value : OptionStarOr < String > ,
) -> std ::result ::Result < OptionStarOr < OffsetDateTime > , InvalidTaskDateError > {
value . try_map ( | x | deserialize_date ( & x , DeserializeDateOption ::Before ) )
2023-01-11 20:33:07 +01:00
}
2022-10-19 12:59:12 +02:00
#[ cfg(test) ]
mod tests {
2023-01-11 20:33:07 +01:00
use deserr ::DeserializeFromValue ;
2022-10-19 12:59:12 +02:00
use meili_snap ::snapshot ;
2023-01-16 16:59:26 +01:00
use meilisearch_types ::deserr ::DeserrQueryParamError ;
2023-01-17 13:17:47 +01:00
use meilisearch_types ::error ::{ Code , ResponseError } ;
2022-10-19 12:59:12 +02:00
2023-01-12 14:09:15 +01:00
use crate ::routes ::tasks ::{ TaskDeletionOrCancelationQuery , TasksFilterQuery } ;
2023-01-11 20:33:07 +01:00
2023-01-17 13:17:47 +01:00
fn deserr_query_params < T > ( j : & str ) -> Result < T , ResponseError >
2023-01-11 20:33:07 +01:00
where
2023-01-12 13:55:53 +01:00
T : DeserializeFromValue < DeserrQueryParamError > ,
2023-01-11 20:33:07 +01:00
{
2023-01-17 13:17:47 +01:00
let value = serde_urlencoded ::from_str ::< serde_json ::Value > ( j )
. map_err ( | e | ResponseError ::from_msg ( e . to_string ( ) , Code ::BadRequest ) ) ? ;
match deserr ::deserialize ::< _ , _ , DeserrQueryParamError > ( value ) {
Ok ( data ) = > Ok ( data ) ,
Err ( e ) = > Err ( ResponseError ::from ( e ) ) ,
}
2023-01-11 20:33:07 +01:00
}
2022-10-20 18:00:07 +02:00
2022-10-19 12:59:12 +02:00
#[ test ]
2022-11-28 16:27:41 +01:00
fn deserialize_task_filter_dates ( ) {
2022-10-19 12:59:12 +02:00
{
2023-01-11 20:33:07 +01:00
let params = " afterEnqueuedAt=2021-12-03&beforeEnqueuedAt=2021-12-03&afterStartedAt=2021-12-03&beforeStartedAt=2021-12-03&afterFinishedAt=2021-12-03&beforeFinishedAt=2021-12-03 " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
2023-01-16 16:59:26 +01:00
snapshot! ( format! ( " {:?} " , query . after_enqueued_at ) , @ " Other(2021-12-04 0:00:00.0 +00:00:00) " ) ;
snapshot! ( format! ( " {:?} " , query . before_enqueued_at ) , @ " Other(2021-12-03 0:00:00.0 +00:00:00) " ) ;
snapshot! ( format! ( " {:?} " , query . after_started_at ) , @ " Other(2021-12-04 0:00:00.0 +00:00:00) " ) ;
snapshot! ( format! ( " {:?} " , query . before_started_at ) , @ " Other(2021-12-03 0:00:00.0 +00:00:00) " ) ;
snapshot! ( format! ( " {:?} " , query . after_finished_at ) , @ " Other(2021-12-04 0:00:00.0 +00:00:00) " ) ;
snapshot! ( format! ( " {:?} " , query . before_finished_at ) , @ " Other(2021-12-03 0:00:00.0 +00:00:00) " ) ;
2022-10-19 12:59:12 +02:00
}
{
2023-01-11 20:33:07 +01:00
let params =
" afterEnqueuedAt=2021-12-03T23:45:23Z&beforeEnqueuedAt=2021-12-03T23:45:23Z " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
2023-01-16 16:59:26 +01:00
snapshot! ( format! ( " {:?} " , query . after_enqueued_at ) , @ " Other(2021-12-03 23:45:23.0 +00:00:00) " ) ;
snapshot! ( format! ( " {:?} " , query . before_enqueued_at ) , @ " Other(2021-12-03 23:45:23.0 +00:00:00) " ) ;
2022-10-19 12:59:12 +02:00
}
{
2023-01-11 20:33:07 +01:00
let params = " afterEnqueuedAt=1997-11-12T09:55:06-06:20 " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
2023-01-16 16:59:26 +01:00
snapshot! ( format! ( " {:?} " , query . after_enqueued_at ) , @ " Other(1997-11-12 9:55:06.0 -06:20:00) " ) ;
2022-10-19 12:59:12 +02:00
}
{
2023-01-11 20:33:07 +01:00
let params = " afterEnqueuedAt=1997-11-12T09:55:06%2B00:00 " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
2023-01-16 16:59:26 +01:00
snapshot! ( format! ( " {:?} " , query . after_enqueued_at ) , @ " Other(1997-11-12 9:55:06.0 +00:00:00) " ) ;
2022-10-19 12:59:12 +02:00
}
{
2023-01-11 20:33:07 +01:00
let params = " afterEnqueuedAt=1997-11-12T09:55:06.200000300Z " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
2023-01-16 16:59:26 +01:00
snapshot! ( format! ( " {:?} " , query . after_enqueued_at ) , @ " Other(1997-11-12 9:55:06.2000003 +00:00:00) " ) ;
}
{
// Stars are allowed in date fields as well
let params = " afterEnqueuedAt=*&beforeStartedAt=*&afterFinishedAt=*&beforeFinishedAt=*&afterStartedAt=*&beforeEnqueuedAt=* " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
snapshot! ( format! ( " {:?} " , query ) , @ " TaskDeletionOrCancelationQuery { uids: None, canceled_by: None, types: None, statuses: None, index_uids: None, after_enqueued_at: Star, before_enqueued_at: Star, after_started_at: Star, before_started_at: Star, after_finished_at: Star, before_finished_at: Star } " ) ;
2022-10-19 12:59:12 +02:00
}
{
2023-01-11 20:33:07 +01:00
let params = " afterFinishedAt=2021 " ;
let err = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap_err ( ) ;
2023-01-17 13:17:47 +01:00
snapshot! ( meili_snap ::json_string! ( err ) , @ r ###"
{
" message " : " Invalid value in parameter `afterFinishedAt`: `2021` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. " ,
" code " : " invalid_task_after_finished_at " ,
" type " : " invalid_request " ,
2023-01-19 15:48:20 +01:00
" link " : " https://docs.meilisearch.com/errors#invalid_task_after_finished_at "
2023-01-17 13:17:47 +01:00
}
" ###);
2022-11-28 16:27:41 +01:00
}
{
2023-01-11 20:33:07 +01:00
let params = " beforeFinishedAt=2021 " ;
let err = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap_err ( ) ;
2023-01-17 13:17:47 +01:00
snapshot! ( meili_snap ::json_string! ( err ) , @ r ###"
{
" message " : " Invalid value in parameter `beforeFinishedAt`: `2021` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. " ,
" code " : " invalid_task_before_finished_at " ,
" type " : " invalid_request " ,
2023-01-19 15:48:20 +01:00
" link " : " https://docs.meilisearch.com/errors#invalid_task_before_finished_at "
2023-01-17 13:17:47 +01:00
}
" ###);
2022-10-19 12:59:12 +02:00
}
{
2023-01-11 20:33:07 +01:00
let params = " afterEnqueuedAt=2021-12 " ;
let err = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap_err ( ) ;
2023-01-17 13:17:47 +01:00
snapshot! ( meili_snap ::json_string! ( err ) , @ r ###"
{
" message " : " Invalid value in parameter `afterEnqueuedAt`: `2021-12` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. " ,
" code " : " invalid_task_after_enqueued_at " ,
" type " : " invalid_request " ,
2023-01-19 15:48:20 +01:00
" link " : " https://docs.meilisearch.com/errors#invalid_task_after_enqueued_at "
2023-01-17 13:17:47 +01:00
}
" ###);
2022-10-19 12:59:12 +02:00
}
2022-10-19 16:07:04 +02:00
2022-10-19 12:59:12 +02:00
{
2023-01-11 20:33:07 +01:00
let params = " beforeEnqueuedAt=2021-12-03T23 " ;
let err = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap_err ( ) ;
2023-01-17 13:17:47 +01:00
snapshot! ( meili_snap ::json_string! ( err ) , @ r ###"
{
" message " : " Invalid value in parameter `beforeEnqueuedAt`: `2021-12-03T23` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. " ,
" code " : " invalid_task_before_enqueued_at " ,
" type " : " invalid_request " ,
2023-01-19 15:48:20 +01:00
" link " : " https://docs.meilisearch.com/errors#invalid_task_before_enqueued_at "
2023-01-17 13:17:47 +01:00
}
" ###);
2022-11-28 16:27:41 +01:00
}
{
2023-01-11 20:33:07 +01:00
let params = " afterStartedAt=2021-12-03T23:45 " ;
let err = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap_err ( ) ;
2023-01-17 13:17:47 +01:00
snapshot! ( meili_snap ::json_string! ( err ) , @ r ###"
{
" message " : " Invalid value in parameter `afterStartedAt`: `2021-12-03T23:45` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. " ,
" code " : " invalid_task_after_started_at " ,
" type " : " invalid_request " ,
2023-01-19 15:48:20 +01:00
" link " : " https://docs.meilisearch.com/errors#invalid_task_after_started_at "
2023-01-17 13:17:47 +01:00
}
" ###);
2023-01-11 20:33:07 +01:00
}
{
let params = " beforeStartedAt=2021-12-03T23:45 " ;
let err = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap_err ( ) ;
2023-01-17 13:17:47 +01:00
snapshot! ( meili_snap ::json_string! ( err ) , @ r ###"
{
" message " : " Invalid value in parameter `beforeStartedAt`: `2021-12-03T23:45` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. " ,
" code " : " invalid_task_before_started_at " ,
" type " : " invalid_request " ,
2023-01-19 15:48:20 +01:00
" link " : " https://docs.meilisearch.com/errors#invalid_task_before_started_at "
2023-01-17 13:17:47 +01:00
}
" ###);
2022-11-28 16:27:41 +01:00
}
}
#[ test ]
fn deserialize_task_filter_uids ( ) {
{
2023-01-11 20:33:07 +01:00
let params = " uids=78,1,12,73 " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
2023-01-16 16:59:26 +01:00
snapshot! ( format! ( " {:?} " , query . uids ) , @ " List([78, 1, 12, 73]) " ) ;
2022-11-28 16:27:41 +01:00
}
{
2023-01-11 20:33:07 +01:00
let params = " uids=1 " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
2023-01-16 16:59:26 +01:00
snapshot! ( format! ( " {:?} " , query . uids ) , @ " List([1]) " ) ;
}
{
let params = " uids=cat,*,dog " ;
let err = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap_err ( ) ;
2023-01-17 13:17:47 +01:00
snapshot! ( meili_snap ::json_string! ( err ) , @ r ###"
{
" message " : " Invalid value in parameter `uids[0]`: could not parse `cat` as a positive integer " ,
" code " : " invalid_task_uids " ,
" type " : " invalid_request " ,
2023-01-19 15:48:20 +01:00
" link " : " https://docs.meilisearch.com/errors#invalid_task_uids "
2023-01-17 13:17:47 +01:00
}
" ###);
2022-11-28 16:27:41 +01:00
}
{
2023-01-11 20:33:07 +01:00
let params = " uids=78,hello,world " ;
let err = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap_err ( ) ;
2023-01-17 13:17:47 +01:00
snapshot! ( meili_snap ::json_string! ( err ) , @ r ###"
{
" message " : " Invalid value in parameter `uids[1]`: could not parse `hello` as a positive integer " ,
" code " : " invalid_task_uids " ,
" type " : " invalid_request " ,
2023-01-19 15:48:20 +01:00
" link " : " https://docs.meilisearch.com/errors#invalid_task_uids "
2023-01-17 13:17:47 +01:00
}
" ###);
2022-11-28 16:27:41 +01:00
}
{
2023-01-11 20:33:07 +01:00
let params = " uids=cat " ;
let err = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap_err ( ) ;
2023-01-17 13:17:47 +01:00
snapshot! ( meili_snap ::json_string! ( err ) , @ r ###"
{
" message " : " Invalid value in parameter `uids`: could not parse `cat` as a positive integer " ,
" code " : " invalid_task_uids " ,
" type " : " invalid_request " ,
2023-01-19 15:48:20 +01:00
" link " : " https://docs.meilisearch.com/errors#invalid_task_uids "
2023-01-17 13:17:47 +01:00
}
" ###);
2022-11-28 16:27:41 +01:00
}
}
#[ test ]
fn deserialize_task_filter_status ( ) {
{
2023-01-11 20:33:07 +01:00
let params = " statuses=succeeded,failed,enqueued,processing,canceled " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
2023-01-16 16:59:26 +01:00
snapshot! ( format! ( " {:?} " , query . statuses ) , @ " List([Succeeded, Failed, Enqueued, Processing, Canceled]) " ) ;
2022-11-28 16:27:41 +01:00
}
{
2023-01-11 20:33:07 +01:00
let params = " statuses=enqueued " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
2023-01-16 16:59:26 +01:00
snapshot! ( format! ( " {:?} " , query . statuses ) , @ " List([Enqueued]) " ) ;
2022-11-28 16:27:41 +01:00
}
{
2023-01-11 20:33:07 +01:00
let params = " statuses=finished " ;
let err = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap_err ( ) ;
2023-01-17 13:17:47 +01:00
snapshot! ( meili_snap ::json_string! ( err ) , @ r ###"
{
" message " : " Invalid value in parameter `statuses`: `finished` is not a valid task status. Available statuses are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. " ,
" code " : " invalid_task_statuses " ,
" type " : " invalid_request " ,
2023-01-19 15:48:20 +01:00
" link " : " https://docs.meilisearch.com/errors#invalid_task_statuses "
2023-01-17 13:17:47 +01:00
}
" ###);
2022-11-28 16:27:41 +01:00
}
}
#[ test ]
fn deserialize_task_filter_types ( ) {
{
2023-01-11 20:33:07 +01:00
let params = " types=documentAdditionOrUpdate,documentDeletion,settingsUpdate,indexCreation,indexDeletion,indexUpdate,indexSwap,taskCancelation,taskDeletion,dumpCreation,snapshotCreation " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
2023-01-16 16:59:26 +01:00
snapshot! ( format! ( " {:?} " , query . types ) , @ " List([DocumentAdditionOrUpdate, DocumentDeletion, SettingsUpdate, IndexCreation, IndexDeletion, IndexUpdate, IndexSwap, TaskCancelation, TaskDeletion, DumpCreation, SnapshotCreation]) " ) ;
2022-11-28 16:27:41 +01:00
}
{
2023-01-11 20:33:07 +01:00
let params = " types=settingsUpdate " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
2023-01-16 16:59:26 +01:00
snapshot! ( format! ( " {:?} " , query . types ) , @ " List([SettingsUpdate]) " ) ;
2022-11-28 16:27:41 +01:00
}
{
2023-01-11 20:33:07 +01:00
let params = " types=createIndex " ;
let err = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap_err ( ) ;
2023-01-17 13:17:47 +01:00
snapshot! ( meili_snap ::json_string! ( err ) , @ r ###"
{
" message " : " Invalid value in parameter `types`: `createIndex` is not a valid task type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`. " ,
" code " : " invalid_task_types " ,
" type " : " invalid_request " ,
2023-01-19 15:48:20 +01:00
" link " : " https://docs.meilisearch.com/errors#invalid_task_types "
2023-01-17 13:17:47 +01:00
}
" ###);
2022-11-28 16:27:41 +01:00
}
}
#[ test ]
fn deserialize_task_filter_index_uids ( ) {
{
2023-01-11 20:33:07 +01:00
let params = " indexUids=toto,tata-78 " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
2023-01-16 16:59:26 +01:00
snapshot! ( format! ( " {:?} " , query . index_uids ) , @ r ### "List([IndexUid("toto"), IndexUid("tata-78")])"### ) ;
2022-11-28 16:27:41 +01:00
}
{
2023-01-11 20:33:07 +01:00
let params = " indexUids=index_a " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
2023-01-16 16:59:26 +01:00
snapshot! ( format! ( " {:?} " , query . index_uids ) , @ r ### "List([IndexUid("index_a")])"### ) ;
2022-11-28 16:27:41 +01:00
}
{
2023-01-11 20:33:07 +01:00
let params = " indexUids=1,hé " ;
let err = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap_err ( ) ;
2023-01-17 13:17:47 +01:00
snapshot! ( meili_snap ::json_string! ( err ) , @ r ###"
{
" message " : " Invalid value in parameter `indexUids[1]`: `hé` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). " ,
" code " : " invalid_index_uid " ,
" type " : " invalid_request " ,
2023-01-19 15:48:20 +01:00
" link " : " https://docs.meilisearch.com/errors#invalid_index_uid "
2023-01-17 13:17:47 +01:00
}
" ###);
2022-11-28 16:27:41 +01:00
}
{
2023-01-11 20:33:07 +01:00
let params = " indexUids=hé " ;
let err = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap_err ( ) ;
2023-01-17 13:17:47 +01:00
snapshot! ( meili_snap ::json_string! ( err ) , @ r ###"
{
" message " : " Invalid value in parameter `indexUids`: `hé` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). " ,
" code " : " invalid_index_uid " ,
" type " : " invalid_request " ,
2023-01-19 15:48:20 +01:00
" link " : " https://docs.meilisearch.com/errors#invalid_index_uid "
2023-01-17 13:17:47 +01:00
}
" ###);
2022-11-28 16:27:41 +01:00
}
}
#[ test ]
fn deserialize_task_filter_general ( ) {
{
2023-01-11 20:33:07 +01:00
let params = " from=12&limit=15&indexUids=toto,tata-78&statuses=succeeded,enqueued&afterEnqueuedAt=2012-04-23&uids=1,2,3 " ;
let query = deserr_query_params ::< TasksFilterQuery > ( params ) . unwrap ( ) ;
2023-01-16 16:59:26 +01:00
snapshot! ( format! ( " {:?} " , query ) , @ r ### "TasksFilterQuery { limit: Param(15), from: Some(Param(12)), uids: List([1, 2, 3]), canceled_by: None, types: None, statuses: List([Succeeded, Enqueued]), index_uids: List([IndexUid("toto"), IndexUid("tata-78")]), after_enqueued_at: Other(2012-04-24 0:00:00.0 +00:00:00), before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None }"### ) ;
2022-11-28 16:27:41 +01:00
}
{
// Stars should translate to `None` in the query
// Verify value of the default limit
2023-01-11 20:33:07 +01:00
let params = " indexUids=*&statuses=succeeded,*&afterEnqueuedAt=2012-04-23&uids=1,2,3 " ;
let query = deserr_query_params ::< TasksFilterQuery > ( params ) . unwrap ( ) ;
2023-01-16 16:59:26 +01:00
snapshot! ( format! ( " {:?} " , query ) , @ " TasksFilterQuery { limit: Param(20), from: None, uids: List([1, 2, 3]), canceled_by: None, types: None, statuses: Star, index_uids: Star, after_enqueued_at: Other(2012-04-24 0:00:00.0 +00:00:00), before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None } " ) ;
2022-11-28 16:27:41 +01:00
}
{
// Stars should also translate to `None` in task deletion/cancelation queries
2023-01-11 20:33:07 +01:00
let params = " indexUids=*&statuses=succeeded,*&afterEnqueuedAt=2012-04-23&uids=1,2,3 " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
2023-01-16 16:59:26 +01:00
snapshot! ( format! ( " {:?} " , query ) , @ " TaskDeletionOrCancelationQuery { uids: List([1, 2, 3]), canceled_by: None, types: None, statuses: Star, index_uids: Star, after_enqueued_at: Other(2012-04-24 0:00:00.0 +00:00:00), before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None } " ) ;
2022-11-28 16:27:41 +01:00
}
{
2023-01-16 16:59:26 +01:00
// Star in from not allowed
let params = " uids=*&from=* " ;
2023-01-11 20:33:07 +01:00
let err = deserr_query_params ::< TasksFilterQuery > ( params ) . unwrap_err ( ) ;
2023-01-17 13:17:47 +01:00
snapshot! ( meili_snap ::json_string! ( err ) , @ r ###"
{
" message " : " Invalid value in parameter `from`: could not parse `*` as a positive integer " ,
" code " : " invalid_task_from " ,
" type " : " invalid_request " ,
2023-01-19 15:48:20 +01:00
" link " : " https://docs.meilisearch.com/errors#invalid_task_from "
2023-01-17 13:17:47 +01:00
}
" ###);
2022-11-28 16:27:41 +01:00
}
{
// From not allowed in task deletion/cancelation queries
2023-01-11 20:33:07 +01:00
let params = " from=12 " ;
let err = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap_err ( ) ;
2023-01-17 13:17:47 +01:00
snapshot! ( meili_snap ::json_string! ( err ) , @ r ###"
{
" message " : " Unknown parameter `from`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` " ,
" code " : " bad_request " ,
" type " : " invalid_request " ,
2023-01-19 15:48:20 +01:00
" link " : " https://docs.meilisearch.com/errors#bad_request "
2023-01-17 13:17:47 +01:00
}
" ###);
2022-10-19 12:59:12 +02:00
}
{
2022-11-28 16:27:41 +01:00
// Limit not allowed in task deletion/cancelation queries
2023-01-11 20:33:07 +01:00
let params = " limit=12 " ;
let err = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap_err ( ) ;
2023-01-17 13:17:47 +01:00
snapshot! ( meili_snap ::json_string! ( err ) , @ r ###"
{
" message " : " Unknown parameter `limit`: expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` " ,
" code " : " bad_request " ,
" type " : " invalid_request " ,
2023-01-19 15:48:20 +01:00
" link " : " https://docs.meilisearch.com/errors#bad_request "
2023-01-17 13:17:47 +01:00
}
" ###);
2023-01-16 16:59:26 +01:00
}
}
#[ test ]
fn deserialize_task_delete_or_cancel_empty ( ) {
{
let params = " " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
assert! ( query . is_empty ( ) ) ;
}
{
let params = " statuses=* " ;
let query = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap ( ) ;
assert! ( ! query . is_empty ( ) ) ;
snapshot! ( format! ( " {query:?} " ) , @ " TaskDeletionOrCancelationQuery { uids: None, canceled_by: None, types: None, statuses: Star, index_uids: None, after_enqueued_at: None, before_enqueued_at: None, after_started_at: None, before_started_at: None, after_finished_at: None, before_finished_at: None } " ) ;
2022-10-19 12:59:12 +02:00
}
}
}