2023-01-11 20:33:07 +01:00
use std ::num ::ParseIntError ;
2022-11-28 16:27:41 +01:00
use std ::str ::FromStr ;
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-11 20:33:07 +01:00
use meilisearch_types ::error ::{ deserr_codes ::* , TakeErrorMessage } ;
use meilisearch_types ::error ::{ DeserrError , 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 } ;
2022-06-06 12:45:52 +02:00
use meilisearch_types ::star_or ::StarOr ;
2022-10-26 12:57:29 +02:00
use meilisearch_types ::tasks ::{
serialize_duration , Details , IndexSwap , Kind , KindWithContent , Status , Task ,
} ;
2022-10-12 03:21:25 +02:00
use serde ::{ Deserialize , Serialize } ;
2022-05-17 16:08:23 +02:00
use serde_cs ::vec ::CS ;
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
2022-10-27 01:00:56 +02:00
use super ::{ fold_star_or , 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
2022-09-27 16:33:37 +02:00
const DEFAULT_LIMIT : fn ( ) -> 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
fn parse_option_cs < T : FromStr > (
s : Option < CS < String > > ,
) -> Result < Option < Vec < T > > , TakeErrorMessage < T ::Err > > {
if let Some ( s ) = s {
s . into_iter ( )
. map ( | s | T ::from_str ( & s ) )
. collect ::< Result < Vec < T > , T ::Err > > ( )
. map_err ( TakeErrorMessage )
. map ( Some )
} else {
Ok ( None )
2022-11-28 16:27:41 +01:00
}
}
2023-01-11 20:33:07 +01:00
fn parse_option_cs_star_or < T : FromStr > (
s : Option < CS < StarOr < String > > > ,
) -> Result < Option < Vec < T > > , TakeErrorMessage < T ::Err > > {
if let Some ( s ) = s . and_then ( fold_star_or ) as Option < Vec < String > > {
s . into_iter ( )
. map ( | s | T ::from_str ( & s ) )
. collect ::< Result < Vec < T > , T ::Err > > ( )
. map_err ( TakeErrorMessage )
. map ( Some )
} else {
Ok ( None )
2022-11-28 16:27:41 +01:00
}
}
2023-01-11 20:33:07 +01:00
fn parse_option_str < T : FromStr > ( s : Option < String > ) -> Result < Option < T > , TakeErrorMessage < T ::Err > > {
if let Some ( s ) = s {
T ::from_str ( & s ) . map_err ( TakeErrorMessage ) . map ( Some )
} else {
Ok ( None )
2022-11-28 16:27:41 +01:00
}
}
2023-01-11 20:33:07 +01:00
fn parse_str < T : FromStr > ( s : String ) -> Result < T , TakeErrorMessage < T ::Err > > {
T ::from_str ( & s ) . map_err ( TakeErrorMessage )
2022-10-19 12:59:12 +02:00
}
2023-01-11 20:33:07 +01:00
#[ derive(Debug, DeserializeFromValue) ]
#[ deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields) ]
2022-07-07 10:56:02 +02:00
pub struct TasksFilterQuery {
2023-01-11 20:33:07 +01:00
#[ deserr(error = DeserrError<InvalidTaskLimit>, default = DEFAULT_LIMIT(), from(String) = parse_str::<u32> -> TakeErrorMessage<ParseIntError>) ]
pub limit : u32 ,
#[ deserr(error = DeserrError<InvalidTaskFrom>, from(Option<String>) = parse_option_str::<TaskId> -> TakeErrorMessage<ParseIntError>) ]
pub from : Option < TaskId > ,
#[ deserr(error = DeserrError<InvalidTaskUids>, from(Option<CS<String>>) = parse_option_cs::<u32> -> TakeErrorMessage<ParseIntError>) ]
pub uids : Option < Vec < u32 > > ,
#[ deserr(error = DeserrError<InvalidTaskCanceledBy>, from(Option<CS<String>>) = parse_option_cs::<u32> -> TakeErrorMessage<ParseIntError>) ]
pub canceled_by : Option < Vec < u32 > > ,
#[ deserr(error = DeserrError<InvalidTaskTypes>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Kind> -> TakeErrorMessage<ResponseError>) ]
pub types : Option < Vec < Kind > > ,
#[ deserr(error = DeserrError<InvalidTaskStatuses>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Status> -> TakeErrorMessage<ResponseError>) ]
pub statuses : Option < Vec < Status > > ,
#[ deserr(error = DeserrError<InvalidIndexUid>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<IndexUid> -> TakeErrorMessage<ResponseError>) ]
pub index_uids : Option < Vec < IndexUid > > ,
#[ deserr(error = DeserrError<InvalidTaskAfterEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>) ]
pub after_enqueued_at : Option < OffsetDateTime > ,
#[ deserr(error = DeserrError<InvalidTaskBeforeEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>) ]
pub before_enqueued_at : Option < OffsetDateTime > ,
#[ deserr(error = DeserrError<InvalidTaskAfterStartedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>) ]
pub after_started_at : Option < OffsetDateTime > ,
#[ deserr(error = DeserrError<InvalidTaskBeforeStartedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>) ]
pub before_started_at : Option < OffsetDateTime > ,
#[ deserr(error = DeserrError<InvalidTaskAfterFinishedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>) ]
pub after_finished_at : Option < OffsetDateTime > ,
#[ deserr(error = DeserrError<InvalidTaskBeforeFinishedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>) ]
pub before_finished_at : Option < OffsetDateTime > ,
2022-05-18 12:07:06 +02:00
}
2023-01-11 20:33:07 +01:00
#[ derive(Deserialize, Debug, DeserializeFromValue) ]
#[ deserr(error = DeserrError, rename_all = camelCase, deny_unknown_fields) ]
2022-11-28 16:27:41 +01:00
pub struct TaskDeletionOrCancelationQuery {
2023-01-11 20:33:07 +01:00
#[ deserr(error = DeserrError<InvalidTaskUids>, from(Option<CS<String>>) = parse_option_cs::<u32> -> TakeErrorMessage<ParseIntError>) ]
pub uids : Option < Vec < u32 > > ,
#[ deserr(error = DeserrError<InvalidTaskCanceledBy>, from(Option<CS<String>>) = parse_option_cs::<u32> -> TakeErrorMessage<ParseIntError>) ]
pub canceled_by : Option < Vec < u32 > > ,
#[ deserr(error = DeserrError<InvalidTaskTypes>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Kind> -> TakeErrorMessage<ResponseError>) ]
pub types : Option < Vec < Kind > > ,
#[ deserr(error = DeserrError<InvalidTaskStatuses>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<Status> -> TakeErrorMessage<ResponseError>) ]
pub statuses : Option < Vec < Status > > ,
#[ deserr(error = DeserrError<InvalidIndexUid>, default = None, from(Option<CS<StarOr<String>>>) = parse_option_cs_star_or::<IndexUid> -> TakeErrorMessage<ResponseError>) ]
pub index_uids : Option < Vec < IndexUid > > ,
#[ deserr(error = DeserrError<InvalidTaskAfterEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>) ]
pub after_enqueued_at : Option < OffsetDateTime > ,
#[ deserr(error = DeserrError<InvalidTaskBeforeEnqueuedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>) ]
pub before_enqueued_at : Option < OffsetDateTime > ,
#[ deserr(error = DeserrError<InvalidTaskAfterStartedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>) ]
pub after_started_at : Option < OffsetDateTime > ,
#[ deserr(error = DeserrError<InvalidTaskBeforeStartedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>) ]
pub before_started_at : Option < OffsetDateTime > ,
#[ deserr(error = DeserrError<InvalidTaskAfterFinishedAt>, default = None, from(Option<String>) = deserialize_date_after -> TakeErrorMessage<InvalidTaskDateError>) ]
pub after_finished_at : Option < OffsetDateTime > ,
#[ deserr(error = DeserrError<InvalidTaskBeforeFinishedAt>, default = None, from(Option<String>) = deserialize_date_before -> TakeErrorMessage<InvalidTaskDateError>) ]
pub before_finished_at : Option < OffsetDateTime > ,
2022-10-18 14:48:40 +02:00
}
async fn cancel_tasks (
index_scheduler : GuardedData < ActionPolicy < { actions ::TASKS_CANCEL } > , Data < IndexScheduler > > ,
2023-01-11 20:33:07 +01:00
params : QueryParameter < TaskDeletionOrCancelationQuery , DeserrError > ,
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 > {
2022-11-28 16:27:41 +01:00
let TaskDeletionOrCancelationQuery {
2023-01-11 20:33:07 +01:00
types ,
uids ,
canceled_by ,
statuses ,
index_uids ,
after_enqueued_at ,
before_enqueued_at ,
after_started_at ,
before_started_at ,
after_finished_at ,
before_finished_at ,
} = params . into_inner ( ) ;
2022-10-18 14:48:40 +02:00
2022-11-28 16:27:41 +01:00
analytics . publish (
" Tasks Canceled " . to_string ( ) ,
json! ( {
" filtered_by_uid " : uids . is_some ( ) ,
" filtered_by_index_uid " : index_uids . is_some ( ) ,
" filtered_by_type " : types . is_some ( ) ,
" filtered_by_status " : statuses . is_some ( ) ,
" filtered_by_canceled_by " : canceled_by . is_some ( ) ,
" filtered_by_before_enqueued_at " : before_enqueued_at . is_some ( ) ,
" filtered_by_after_enqueued_at " : after_enqueued_at . is_some ( ) ,
" filtered_by_before_started_at " : before_started_at . is_some ( ) ,
" filtered_by_after_started_at " : after_started_at . is_some ( ) ,
" filtered_by_before_finished_at " : before_finished_at . is_some ( ) ,
" filtered_by_after_finished_at " : after_finished_at . is_some ( ) ,
} ) ,
Some ( & req ) ,
) ;
2022-10-18 14:48:40 +02:00
let query = Query {
limit : None ,
from : None ,
2022-11-28 16:27:41 +01:00
statuses ,
types ,
2023-01-11 20:33:07 +01:00
index_uids : index_uids . map ( | xs | xs . into_iter ( ) . map ( | s | s . to_string ( ) ) . collect ( ) ) ,
2022-11-28 16:27:41 +01:00
uids ,
canceled_by ,
2022-10-19 12:59:12 +02:00
before_enqueued_at ,
after_enqueued_at ,
before_started_at ,
after_started_at ,
before_finished_at ,
after_finished_at ,
2022-10-18 14:48:40 +02:00
} ;
if query . is_empty ( ) {
return Err ( index_scheduler ::Error ::TaskCancelationWithEmptyQuery . into ( ) ) ;
}
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-11 20:33:07 +01:00
params : QueryParameter < TaskDeletionOrCancelationQuery , DeserrError > ,
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 > {
2022-11-28 16:27:41 +01:00
let TaskDeletionOrCancelationQuery {
2023-01-11 20:33:07 +01:00
types ,
uids ,
canceled_by ,
statuses ,
index_uids ,
after_enqueued_at ,
before_enqueued_at ,
after_started_at ,
before_started_at ,
after_finished_at ,
before_finished_at ,
} = params . into_inner ( ) ;
2022-10-13 12:48:23 +02:00
2022-11-28 16:27:41 +01:00
analytics . publish (
" Tasks Deleted " . to_string ( ) ,
json! ( {
" filtered_by_uid " : uids . is_some ( ) ,
" filtered_by_index_uid " : index_uids . is_some ( ) ,
" filtered_by_type " : types . is_some ( ) ,
" filtered_by_status " : statuses . is_some ( ) ,
" filtered_by_canceled_by " : canceled_by . is_some ( ) ,
" filtered_by_before_enqueued_at " : before_enqueued_at . is_some ( ) ,
" filtered_by_after_enqueued_at " : after_enqueued_at . is_some ( ) ,
" filtered_by_before_started_at " : before_started_at . is_some ( ) ,
" filtered_by_after_started_at " : after_started_at . is_some ( ) ,
" filtered_by_before_finished_at " : before_finished_at . is_some ( ) ,
" filtered_by_after_finished_at " : after_finished_at . is_some ( ) ,
} ) ,
Some ( & req ) ,
) ;
2022-10-13 12:48:23 +02:00
let query = Query {
limit : None ,
from : None ,
2022-11-28 16:27:41 +01:00
statuses ,
types ,
2023-01-11 20:33:07 +01:00
index_uids : index_uids . map ( | xs | xs . into_iter ( ) . map ( | s | s . to_string ( ) ) . collect ( ) ) ,
2022-11-28 16:27:41 +01:00
uids ,
canceled_by ,
2022-10-19 12:59:12 +02:00
after_enqueued_at ,
before_enqueued_at ,
after_started_at ,
before_started_at ,
after_finished_at ,
before_finished_at ,
2022-10-13 12:48:23 +02:00
} ;
2022-10-18 14:48:40 +02:00
2022-10-15 11:17:06 +02:00
if query . is_empty ( ) {
return Err ( index_scheduler ::Error ::TaskDeletionWithEmptyQuery . into ( ) ) ;
}
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-11 20:33:07 +01:00
params : QueryParameter < TasksFilterQuery , DeserrError > ,
2021-12-02 16:03:26 +01:00
req : HttpRequest ,
analytics : web ::Data < dyn Analytics > ,
) -> Result < HttpResponse , ResponseError > {
2023-01-11 20:33:07 +01:00
let params = params . into_inner ( ) ;
2022-11-28 16:27:41 +01:00
analytics . get_tasks ( & params , & req ) ;
2022-07-07 10:56:02 +02:00
let TasksFilterQuery {
2023-01-11 20:33:07 +01:00
types ,
uids ,
canceled_by ,
statuses ,
index_uids ,
2022-05-31 11:56:51 +02:00
limit ,
2022-06-01 15:30:39 +02:00
from ,
2023-01-11 20:33:07 +01:00
after_enqueued_at ,
before_enqueued_at ,
after_started_at ,
before_started_at ,
after_finished_at ,
before_finished_at ,
} = params ;
2022-07-07 10:56:02 +02:00
2022-05-31 11:56:51 +02:00
// We +1 just to know if there is more after this "page" or not.
2022-06-01 12:04:01 +02:00
let limit = limit . saturating_add ( 1 ) ;
2022-10-19 12:59:12 +02:00
let query = index_scheduler ::Query {
limit : Some ( limit ) ,
from ,
2022-11-28 16:27:41 +01:00
statuses ,
types ,
2023-01-11 20:33:07 +01:00
index_uids : index_uids . map ( | xs | xs . into_iter ( ) . map ( | s | s . to_string ( ) ) . collect ( ) ) ,
2022-11-28 16:27:41 +01:00
uids ,
canceled_by ,
2022-10-19 12:59:12 +02:00
before_enqueued_at ,
after_enqueued_at ,
before_started_at ,
after_started_at ,
before_finished_at ,
after_finished_at ,
} ;
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 ,
) -> std ::result ::Result < OffsetDateTime , TakeErrorMessage < InvalidTaskDateError > > {
// 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 {
Err ( TakeErrorMessage ( InvalidTaskDateError ( value . to_owned ( ) ) ) )
}
}
pub fn deserialize_date_before (
value : Option < String > ,
) -> std ::result ::Result < Option < OffsetDateTime > , TakeErrorMessage < InvalidTaskDateError > > {
if let Some ( value ) = value {
let date = deserialize_date ( & value , DeserializeDateOption ::Before ) ? ;
Ok ( Some ( date ) )
} else {
Ok ( None )
}
}
pub fn deserialize_date_after (
value : Option < String > ,
) -> std ::result ::Result < Option < OffsetDateTime > , TakeErrorMessage < InvalidTaskDateError > > {
if let Some ( value ) = value {
let date = deserialize_date ( & value , DeserializeDateOption ::After ) ? ;
Ok ( Some ( date ) )
} else {
Ok ( None )
2022-10-19 12:59:12 +02:00
}
}
2023-01-11 20:33:07 +01:00
#[ derive(Debug) ]
pub struct InvalidTaskDateError ( String ) ;
impl std ::fmt ::Display for InvalidTaskDateError {
fn fmt ( & self , f : & mut std ::fmt ::Formatter < '_ > ) -> std ::fmt ::Result {
write! ( f , " `{}` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. " , self . 0 )
}
}
impl std ::error ::Error for InvalidTaskDateError { }
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-11 20:33:07 +01:00
use meilisearch_types ::error ::DeserrError ;
2022-10-19 12:59:12 +02:00
2023-01-11 20:33:07 +01:00
use crate ::{
extractors ::query_parameters ::QueryParameter ,
routes ::tasks ::{ TaskDeletionOrCancelationQuery , TasksFilterQuery } ,
} ;
fn deserr_query_params < T > ( j : & str ) -> Result < T , actix_web ::Error >
where
T : DeserializeFromValue < DeserrError > ,
{
QueryParameter ::< T , DeserrError > ::from_query ( j ) . map ( | p | p . 0 )
}
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 ( ) ;
snapshot! ( format! ( " {:?} " , query . after_enqueued_at . unwrap ( ) ) , @ " 2021-12-04 0:00:00.0 +00:00:00 " ) ;
snapshot! ( format! ( " {:?} " , query . before_enqueued_at . unwrap ( ) ) , @ " 2021-12-03 0:00:00.0 +00:00:00 " ) ;
snapshot! ( format! ( " {:?} " , query . after_started_at . unwrap ( ) ) , @ " 2021-12-04 0:00:00.0 +00:00:00 " ) ;
snapshot! ( format! ( " {:?} " , query . before_started_at . unwrap ( ) ) , @ " 2021-12-03 0:00:00.0 +00:00:00 " ) ;
snapshot! ( format! ( " {:?} " , query . after_finished_at . unwrap ( ) ) , @ " 2021-12-04 0:00:00.0 +00:00:00 " ) ;
snapshot! ( format! ( " {:?} " , query . before_finished_at . unwrap ( ) ) , @ " 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 ( ) ;
snapshot! ( format! ( " {:?} " , query . after_enqueued_at . unwrap ( ) ) , @ " 2021-12-03 23:45:23.0 +00:00:00 " ) ;
snapshot! ( format! ( " {:?} " , query . before_enqueued_at . unwrap ( ) ) , @ " 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 ( ) ;
snapshot! ( format! ( " {:?} " , query . after_enqueued_at . unwrap ( ) ) , @ " 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 ( ) ;
snapshot! ( format! ( " {:?} " , query . after_enqueued_at . unwrap ( ) ) , @ " 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 ( ) ;
snapshot! ( format! ( " {:?} " , query . after_enqueued_at . unwrap ( ) ) , @ " 1997-11-12 9:55:06.2000003 +00:00:00 " ) ;
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 ( ) ;
snapshot! ( format! ( " {err} " ) , @ " `2021` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterFinishedAt`. " ) ;
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 ( ) ;
snapshot! ( format! ( " {err} " ) , @ " `2021` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeFinishedAt`. " ) ;
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 ( ) ;
snapshot! ( format! ( " {err} " ) , @ " `2021-12` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterEnqueuedAt`. " ) ;
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 ( ) ;
snapshot! ( format! ( " {err} " ) , @ " `2021-12-03T23` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeEnqueuedAt`. " ) ;
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 ( ) ;
snapshot! ( format! ( " {err} " ) , @ " `2021-12-03T23:45` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.afterStartedAt`. " ) ;
}
{
let params = " beforeStartedAt=2021-12-03T23:45 " ;
let err = deserr_query_params ::< TaskDeletionOrCancelationQuery > ( params ) . unwrap_err ( ) ;
snapshot! ( format! ( " {err} " ) , @ " `2021-12-03T23:45` is an invalid date-time. It should follow the YYYY-MM-DD or RFC 3339 date-time format. at `.beforeStartedAt`. " ) ;
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 ( ) ;
snapshot! ( format! ( " {:?} " , query . uids . unwrap ( ) ) , @ " [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 ( ) ;
snapshot! ( format! ( " {:?} " , query . uids . unwrap ( ) ) , @ " [1] " ) ;
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 ( ) ;
snapshot! ( format! ( " {err} " ) , @ " invalid digit found in string at `.uids`. " ) ;
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 ( ) ;
snapshot! ( format! ( " {err} " ) , @ " invalid digit found in string at `.uids`. " ) ;
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 ( ) ;
snapshot! ( format! ( " {:?} " , query . statuses . unwrap ( ) ) , @ " [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 ( ) ;
snapshot! ( format! ( " {:?} " , query . statuses . unwrap ( ) ) , @ " [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 ( ) ;
snapshot! ( format! ( " {err} " ) , @ " `finished` is not a status. Available status are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. at `.statuses`. " ) ;
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 ( ) ;
snapshot! ( format! ( " {:?} " , query . types . unwrap ( ) ) , @ " [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 ( ) ;
snapshot! ( format! ( " {:?} " , query . types . unwrap ( ) ) , @ " [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 ( ) ;
snapshot! ( format! ( " {err} " ) , @ " `createIndex` is not a type. Available types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation`. at `.types`. " ) ;
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 ( ) ;
snapshot! ( format! ( " {:?} " , query . index_uids . unwrap ( ) ) , @ r ### "[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 ( ) ;
snapshot! ( format! ( " {:?} " , query . index_uids . unwrap ( ) ) , @ r ### "[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 ( ) ;
snapshot! ( format! ( " {err} " ) , @ " `hé` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). at `.indexUids`. " ) ;
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 ( ) ;
snapshot! ( format! ( " {err} " ) , @ " `hé` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). at `.indexUids`. " ) ;
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 ( ) ;
snapshot! ( format! ( " {:?} " , query ) , @ r ### "TasksFilterQuery { limit: 15, from: Some(12), uids: Some([1, 2, 3]), canceled_by: None, types: None, statuses: Some([Succeeded, Enqueued]), index_uids: Some([IndexUid("toto"), IndexUid("tata-78")]), after_enqueued_at: Some(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 ( ) ;
snapshot! ( format! ( " {:?} " , query ) , @ " TasksFilterQuery { limit: 20, from: None, uids: Some([1, 2, 3]), canceled_by: None, types: None, statuses: None, index_uids: None, after_enqueued_at: Some(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 ( ) ;
snapshot! ( format! ( " {:?} " , query ) , @ " TaskDeletionOrCancelationQuery { uids: Some([1, 2, 3]), canceled_by: None, types: None, statuses: None, index_uids: None, after_enqueued_at: Some(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 in uids not allowed
2023-01-11 20:33:07 +01:00
let params = " uids=* " ;
let err = deserr_query_params ::< TasksFilterQuery > ( params ) . unwrap_err ( ) ;
snapshot! ( format! ( " {err} " ) , @ " invalid digit found in string at `.uids`. " ) ;
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 ( ) ;
snapshot! ( format! ( " {err} " ) , @ " Json deserialize error: unknown field `from`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``. " ) ;
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 ( ) ;
snapshot! ( format! ( " {err} " ) , @ " Json deserialize error: unknown field `limit`, expected one of `uids`, `canceledBy`, `types`, `statuses`, `indexUids`, `afterEnqueuedAt`, `beforeEnqueuedAt`, `afterStartedAt`, `beforeStartedAt`, `afterFinishedAt`, `beforeFinishedAt` at ``. " ) ;
2022-10-19 12:59:12 +02:00
}
}
}