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 } ;
2022-12-14 13:00:43 +01:00
use index_scheduler ::error ::DateField ;
2022-10-13 11:09:00 +02:00
use index_scheduler ::{ IndexScheduler , Query , TaskId } ;
2022-06-06 12:38:46 +02:00
use meilisearch_types ::error ::ResponseError ;
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 ;
2022-10-12 03:21:25 +02:00
use time ::{ Duration , OffsetDateTime } ;
2022-10-26 11:11:53 +02:00
use tokio ::task ;
2021-12-02 16:03:26 +01:00
2022-11-28 16:27:41 +01:00
use self ::date_deserializer ::{ deserialize_date , DeserializeDateOption } ;
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 ;
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
}
}
}
2022-11-28 16:27:41 +01:00
#[ derive(Deserialize, Debug) ]
#[ serde(rename_all = " camelCase " , deny_unknown_fields) ]
pub struct TaskCommonQueryRaw {
pub uids : Option < CS < String > > ,
pub canceled_by : Option < CS < String > > ,
pub types : Option < CS < StarOr < String > > > ,
pub statuses : Option < CS < StarOr < String > > > ,
pub index_uids : Option < CS < StarOr < String > > > ,
}
2022-12-14 13:00:43 +01:00
2022-11-28 16:27:41 +01:00
impl TaskCommonQueryRaw {
fn validate ( self ) -> Result < TaskCommonQuery , ResponseError > {
let Self { uids , canceled_by , types , statuses , index_uids } = self ;
let uids = if let Some ( uids ) = uids {
Some (
uids . into_iter ( )
. map ( | uid_string | {
uid_string . parse ::< u32 > ( ) . map_err ( | _e | {
index_scheduler ::Error ::InvalidTaskUids { task_uid : uid_string } . into ( )
} )
} )
. collect ::< Result < Vec < u32 > , ResponseError > > ( ) ? ,
)
} else {
None
} ;
let canceled_by = if let Some ( canceled_by ) = canceled_by {
Some (
canceled_by
. into_iter ( )
. map ( | canceled_by_string | {
canceled_by_string . parse ::< u32 > ( ) . map_err ( | _e | {
index_scheduler ::Error ::InvalidTaskCanceledBy {
canceled_by : canceled_by_string ,
}
. into ( )
} )
} )
. collect ::< Result < Vec < u32 > , ResponseError > > ( ) ? ,
)
} else {
None
} ;
let types = if let Some ( types ) = types . and_then ( fold_star_or ) as Option < Vec < String > > {
Some (
types
. into_iter ( )
. map ( | type_string | {
Kind ::from_str ( & type_string ) . map_err ( | _e | {
index_scheduler ::Error ::InvalidTaskTypes { type_ : type_string } . into ( )
} )
} )
. collect ::< Result < Vec < Kind > , ResponseError > > ( ) ? ,
)
} else {
None
} ;
let statuses = if let Some ( statuses ) =
statuses . and_then ( fold_star_or ) as Option < Vec < String > >
{
Some (
statuses
. into_iter ( )
. map ( | status_string | {
Status ::from_str ( & status_string ) . map_err ( | _e | {
index_scheduler ::Error ::InvalidTaskStatuses { status : status_string }
. into ( )
} )
} )
. collect ::< Result < Vec < Status > , ResponseError > > ( ) ? ,
)
} else {
None
} ;
let index_uids =
if let Some ( index_uids ) = index_uids . and_then ( fold_star_or ) as Option < Vec < String > > {
Some (
index_uids
. into_iter ( )
. map ( | index_uid_string | {
IndexUid ::from_str ( & index_uid_string )
. map ( | index_uid | index_uid . to_string ( ) )
. map_err ( | _e | {
index_scheduler ::Error ::InvalidIndexUid {
index_uid : index_uid_string ,
}
. into ( )
} )
} )
. collect ::< Result < Vec < String > , ResponseError > > ( ) ? ,
)
} else {
None
} ;
Ok ( TaskCommonQuery { types , uids , canceled_by , statuses , index_uids } )
}
}
#[ derive(Deserialize, Debug) ]
#[ serde(rename_all = " camelCase " , deny_unknown_fields) ]
pub struct TaskDateQueryRaw {
pub after_enqueued_at : Option < String > ,
pub before_enqueued_at : Option < String > ,
pub after_started_at : Option < String > ,
pub before_started_at : Option < String > ,
pub after_finished_at : Option < String > ,
pub before_finished_at : Option < String > ,
}
2023-01-11 18:50:46 +01:00
2022-11-28 16:27:41 +01:00
impl TaskDateQueryRaw {
fn validate ( self ) -> Result < TaskDateQuery , ResponseError > {
let Self {
after_enqueued_at ,
before_enqueued_at ,
after_started_at ,
before_started_at ,
after_finished_at ,
before_finished_at ,
} = self ;
let mut query = TaskDateQuery {
after_enqueued_at : None ,
before_enqueued_at : None ,
after_started_at : None ,
before_started_at : None ,
after_finished_at : None ,
before_finished_at : None ,
} ;
for ( field_name , string_value , before_or_after , dest ) in [
(
2022-12-14 13:00:43 +01:00
DateField ::AfterEnqueuedAt ,
2022-11-28 16:27:41 +01:00
after_enqueued_at ,
DeserializeDateOption ::After ,
& mut query . after_enqueued_at ,
) ,
(
2022-12-14 13:00:43 +01:00
DateField ::BeforeEnqueuedAt ,
2022-11-28 16:27:41 +01:00
before_enqueued_at ,
DeserializeDateOption ::Before ,
& mut query . before_enqueued_at ,
) ,
(
2022-12-14 13:00:43 +01:00
DateField ::AfterStartedAt ,
2022-11-28 16:27:41 +01:00
after_started_at ,
DeserializeDateOption ::After ,
& mut query . after_started_at ,
) ,
(
2022-12-14 13:00:43 +01:00
DateField ::BeforeStartedAt ,
2022-11-28 16:27:41 +01:00
before_started_at ,
DeserializeDateOption ::Before ,
& mut query . before_started_at ,
) ,
(
2022-12-14 13:00:43 +01:00
DateField ::AfterFinishedAt ,
2022-11-28 16:27:41 +01:00
after_finished_at ,
DeserializeDateOption ::After ,
& mut query . after_finished_at ,
) ,
(
2022-12-14 13:00:43 +01:00
DateField ::BeforeFinishedAt ,
2022-11-28 16:27:41 +01:00
before_finished_at ,
DeserializeDateOption ::Before ,
& mut query . before_finished_at ,
) ,
] {
if let Some ( string_value ) = string_value {
* dest = Some ( deserialize_date ( field_name , & string_value , before_or_after ) ? ) ;
}
}
Ok ( query )
}
}
#[ derive(Deserialize, Debug) ]
#[ serde(rename_all = " camelCase " , deny_unknown_fields) ]
pub struct TasksFilterQueryRaw {
#[ serde(flatten) ]
pub common : TaskCommonQueryRaw ,
#[ serde(default = " DEFAULT_LIMIT " ) ]
pub limit : u32 ,
pub from : Option < TaskId > ,
#[ serde(flatten) ]
pub dates : TaskDateQueryRaw ,
}
#[ derive(Deserialize, Debug) ]
#[ serde(rename_all = " camelCase " , deny_unknown_fields) ]
pub struct TaskDeletionOrCancelationQueryRaw {
#[ serde(flatten) ]
pub common : TaskCommonQueryRaw ,
#[ serde(flatten) ]
pub dates : TaskDateQueryRaw ,
}
impl TasksFilterQueryRaw {
fn validate ( self ) -> Result < TasksFilterQuery , ResponseError > {
let Self { common , limit , from , dates } = self ;
let common = common . validate ( ) ? ;
let dates = dates . validate ( ) ? ;
Ok ( TasksFilterQuery { common , limit , from , dates } )
}
}
impl TaskDeletionOrCancelationQueryRaw {
fn validate ( self ) -> Result < TaskDeletionOrCancelationQuery , ResponseError > {
let Self { common , dates } = self ;
let common = common . validate ( ) ? ;
let dates = dates . validate ( ) ? ;
Ok ( TaskDeletionOrCancelationQuery { common , dates } )
}
}
#[ derive(Serialize, Debug) ]
2022-10-19 12:59:12 +02:00
#[ serde(rename_all = " camelCase " , deny_unknown_fields) ]
pub struct TaskDateQuery {
#[ serde(
default ,
skip_serializing_if = " Option::is_none " ,
2022-11-28 16:27:41 +01:00
serialize_with = " time::serde::rfc3339::option::serialize "
2022-10-19 12:59:12 +02:00
) ]
after_enqueued_at : Option < OffsetDateTime > ,
#[ serde(
default ,
skip_serializing_if = " Option::is_none " ,
2022-11-28 16:27:41 +01:00
serialize_with = " time::serde::rfc3339::option::serialize "
2022-10-19 12:59:12 +02:00
) ]
before_enqueued_at : Option < OffsetDateTime > ,
#[ serde(
default ,
skip_serializing_if = " Option::is_none " ,
2022-11-28 16:27:41 +01:00
serialize_with = " time::serde::rfc3339::option::serialize "
2022-10-19 12:59:12 +02:00
) ]
after_started_at : Option < OffsetDateTime > ,
#[ serde(
default ,
skip_serializing_if = " Option::is_none " ,
2022-11-28 16:27:41 +01:00
serialize_with = " time::serde::rfc3339::option::serialize "
2022-10-19 12:59:12 +02:00
) ]
before_started_at : Option < OffsetDateTime > ,
#[ serde(
default ,
skip_serializing_if = " Option::is_none " ,
2022-11-28 16:27:41 +01:00
serialize_with = " time::serde::rfc3339::option::serialize "
2022-10-19 12:59:12 +02:00
) ]
after_finished_at : Option < OffsetDateTime > ,
#[ serde(
default ,
skip_serializing_if = " Option::is_none " ,
2022-11-28 16:27:41 +01:00
serialize_with = " time::serde::rfc3339::option::serialize "
2022-10-19 12:59:12 +02:00
) ]
before_finished_at : Option < OffsetDateTime > ,
}
2022-11-28 16:27:41 +01:00
#[ derive(Debug) ]
pub struct TaskCommonQuery {
types : Option < Vec < Kind > > ,
uids : Option < Vec < TaskId > > ,
canceled_by : Option < Vec < TaskId > > ,
statuses : Option < Vec < Status > > ,
index_uids : Option < Vec < String > > ,
}
#[ derive(Debug) ]
2022-07-07 10:56:02 +02:00
pub struct TasksFilterQuery {
2022-09-27 16:33:37 +02:00
limit : u32 ,
2022-06-01 15:30:39 +02:00
from : Option < TaskId > ,
2022-11-28 16:27:41 +01:00
common : TaskCommonQuery ,
2022-10-19 12:59:12 +02:00
dates : TaskDateQuery ,
2022-05-18 12:07:06 +02:00
}
2022-11-28 16:27:41 +01:00
#[ derive(Debug) ]
pub struct TaskDeletionOrCancelationQuery {
common : TaskCommonQuery ,
2022-10-19 12:59:12 +02:00
dates : TaskDateQuery ,
2022-10-18 14:48:40 +02:00
}
async fn cancel_tasks (
index_scheduler : GuardedData < ActionPolicy < { actions ::TASKS_CANCEL } > , Data < IndexScheduler > > ,
2022-11-28 16:27:41 +01:00
params : web ::Query < TaskDeletionOrCancelationQueryRaw > ,
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 query = params . into_inner ( ) . validate ( ) ? ;
let TaskDeletionOrCancelationQuery {
common : TaskCommonQuery { types , uids , canceled_by , statuses , index_uids } ,
2022-10-19 12:59:12 +02:00
dates :
TaskDateQuery {
after_enqueued_at ,
before_enqueued_at ,
after_started_at ,
before_started_at ,
after_finished_at ,
before_finished_at ,
} ,
2022-11-28 16:27:41 +01:00
} = query ;
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 ,
index_uids ,
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 > > ,
2022-11-28 16:27:41 +01:00
params : web ::Query < TaskDeletionOrCancelationQueryRaw > ,
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 {
common : TaskCommonQuery { types , uids , canceled_by , statuses , index_uids } ,
2022-10-19 12:59:12 +02:00
dates :
TaskDateQuery {
after_enqueued_at ,
before_enqueued_at ,
after_started_at ,
before_started_at ,
after_finished_at ,
before_finished_at ,
} ,
2022-11-28 16:27:41 +01:00
} = params . into_inner ( ) . validate ( ) ? ;
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 ,
index_uids ,
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 > > ,
2022-11-28 16:27:41 +01:00
params : web ::Query < TasksFilterQueryRaw > ,
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
analytics . get_tasks ( & params , & req ) ;
2022-07-07 10:56:02 +02:00
let TasksFilterQuery {
2022-11-28 16:27:41 +01:00
common : TaskCommonQuery { 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 ,
2022-10-19 12:59:12 +02:00
dates :
TaskDateQuery {
after_enqueued_at ,
before_enqueued_at ,
after_started_at ,
before_started_at ,
after_finished_at ,
before_finished_at ,
} ,
2022-11-28 16:27:41 +01:00
} = params . into_inner ( ) . validate ( ) ? ;
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 ,
index_uids ,
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
2022-10-19 16:07:04 +02:00
pub ( crate ) mod date_deserializer {
2022-12-14 13:00:43 +01:00
use index_scheduler ::error ::DateField ;
2022-11-28 16:27:41 +01:00
use meilisearch_types ::error ::ResponseError ;
2022-10-20 18:00:07 +02:00
use time ::format_description ::well_known ::Rfc3339 ;
use time ::macros ::format_description ;
use time ::{ Date , Duration , OffsetDateTime , Time } ;
2022-10-19 16:07:04 +02:00
2022-11-28 16:27:41 +01:00
pub enum DeserializeDateOption {
2022-10-19 16:07:04 +02:00
Before ,
After ,
2022-10-19 12:59:12 +02:00
}
2022-11-28 16:27:41 +01:00
pub fn deserialize_date (
2022-12-14 13:00:43 +01:00
field_name : DateField ,
2022-10-19 16:07:04 +02:00
value : & str ,
option : DeserializeDateOption ,
2022-11-28 16:27:41 +01:00
) -> std ::result ::Result < OffsetDateTime , ResponseError > {
2022-10-19 16:07:04 +02: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 = > {
2022-11-28 16:27:41 +01:00
let datetime = datetime . checked_add ( Duration ::days ( 1 ) ) . unwrap_or ( datetime ) ;
2022-10-19 16:07:04 +02:00
Ok ( datetime )
}
}
} else {
2022-11-28 16:27:41 +01:00
Err ( index_scheduler ::Error ::InvalidTaskDate {
2022-12-14 13:00:43 +01:00
field : field_name ,
2022-11-28 16:27:41 +01:00
date : value . to_string ( ) ,
2022-10-19 16:07:04 +02:00
}
2022-11-28 16:27:41 +01:00
. into ( ) )
2022-10-19 12:59:12 +02:00
}
}
}
#[ cfg(test) ]
mod tests {
use meili_snap ::snapshot ;
2022-11-28 16:27:41 +01:00
use crate ::routes ::tasks ::{ TaskDeletionOrCancelationQueryRaw , TasksFilterQueryRaw } ;
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
{
2022-10-19 16:07:04 +02:00
let json = r #" {
" afterEnqueuedAt " : " 2021-12-03 " ,
" beforeEnqueuedAt " : " 2021-12-03 " ,
" afterStartedAt " : " 2021-12-03 " ,
" beforeStartedAt " : " 2021-12-03 " ,
" afterFinishedAt " : " 2021-12-03 " ,
" beforeFinishedAt " : " 2021-12-03 "
} " #;
2022-11-28 16:27:41 +01:00
let query = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap ( ) ;
2022-10-19 16:07:04 +02:00
snapshot! ( format! ( " {:?} " , query . dates . after_enqueued_at . unwrap ( ) ) , @ " 2021-12-04 0:00:00.0 +00:00:00 " ) ;
snapshot! ( format! ( " {:?} " , query . dates . before_enqueued_at . unwrap ( ) ) , @ " 2021-12-03 0:00:00.0 +00:00:00 " ) ;
snapshot! ( format! ( " {:?} " , query . dates . after_started_at . unwrap ( ) ) , @ " 2021-12-04 0:00:00.0 +00:00:00 " ) ;
snapshot! ( format! ( " {:?} " , query . dates . before_started_at . unwrap ( ) ) , @ " 2021-12-03 0:00:00.0 +00:00:00 " ) ;
snapshot! ( format! ( " {:?} " , query . dates . after_finished_at . unwrap ( ) ) , @ " 2021-12-04 0:00:00.0 +00:00:00 " ) ;
snapshot! ( format! ( " {:?} " , query . dates . before_finished_at . unwrap ( ) ) , @ " 2021-12-03 0:00:00.0 +00:00:00 " ) ;
2022-10-19 12:59:12 +02:00
}
{
2022-10-19 16:07:04 +02:00
let json = r # " { "afterEnqueuedAt": "2021-12-03T23:45:23Z", "beforeEnqueuedAt": "2021-12-03T23:45:23Z" } "# ;
2022-11-28 16:27:41 +01:00
let query = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap ( ) ;
2022-10-19 16:07:04 +02:00
snapshot! ( format! ( " {:?} " , query . dates . after_enqueued_at . unwrap ( ) ) , @ " 2021-12-03 23:45:23.0 +00:00:00 " ) ;
snapshot! ( format! ( " {:?} " , query . dates . before_enqueued_at . unwrap ( ) ) , @ " 2021-12-03 23:45:23.0 +00:00:00 " ) ;
2022-10-19 12:59:12 +02:00
}
{
2022-10-19 16:07:04 +02:00
let json = r # " { "afterEnqueuedAt": "1997-11-12T09:55:06-06:20" } "# ;
2022-11-28 16:27:41 +01:00
let query = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap ( ) ;
2022-10-19 16:07:04 +02:00
snapshot! ( format! ( " {:?} " , query . dates . after_enqueued_at . unwrap ( ) ) , @ " 1997-11-12 9:55:06.0 -06:20:00 " ) ;
2022-10-19 12:59:12 +02:00
}
{
2022-10-19 16:07:04 +02:00
let json = r # " { "afterEnqueuedAt": "1997-11-12T09:55:06+00:00" } "# ;
2022-11-28 16:27:41 +01:00
let query = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap ( ) ;
2022-10-19 16:07:04 +02:00
snapshot! ( format! ( " {:?} " , query . dates . after_enqueued_at . unwrap ( ) ) , @ " 1997-11-12 9:55:06.0 +00:00:00 " ) ;
2022-10-19 12:59:12 +02:00
}
{
2022-10-19 16:07:04 +02:00
let json = r # " { "afterEnqueuedAt": "1997-11-12T09:55:06.200000300Z" } "# ;
2022-11-28 16:27:41 +01:00
let query = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap ( ) ;
2022-10-19 16:07:04 +02:00
snapshot! ( format! ( " {:?} " , query . dates . after_enqueued_at . unwrap ( ) ) , @ " 1997-11-12 9:55:06.2000003 +00:00:00 " ) ;
2022-10-19 12:59:12 +02:00
}
{
2022-11-28 16:27:41 +01:00
let json = r # " { "afterFinishedAt": "2021" } "# ;
let err = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap_err ( ) ;
snapshot! ( format! ( " {err} " ) , @ " Task `afterFinishedAt` `2021` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format. " ) ;
}
{
let json = r # " { "beforeFinishedAt": "2021" } "# ;
let err = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap_err ( ) ;
snapshot! ( format! ( " {err} " ) , @ " Task `beforeFinishedAt` `2021` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format. " ) ;
2022-10-19 12:59:12 +02:00
}
{
2022-10-19 16:07:04 +02:00
let json = r # " { "afterEnqueuedAt": "2021-12" } "# ;
2022-11-28 16:27:41 +01:00
let err = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap_err ( ) ;
snapshot! ( format! ( " {err} " ) , @ " Task `afterEnqueuedAt` `2021-12` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format. " ) ;
2022-10-19 12:59:12 +02:00
}
2022-10-19 16:07:04 +02:00
2022-10-19 12:59:12 +02:00
{
2022-11-28 16:27:41 +01:00
let json = r # " { "beforeEnqueuedAt": "2021-12-03T23" } "# ;
let err = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap_err ( ) ;
snapshot! ( format! ( " {err} " ) , @ " Task `beforeEnqueuedAt` `2021-12-03T23` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format. " ) ;
}
{
let json = r # " { "afterStartedAt": "2021-12-03T23:45" } "# ;
let err = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap_err ( ) ;
snapshot! ( format! ( " {err} " ) , @ " Task `afterStartedAt` `2021-12-03T23:45` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format. " ) ;
let json = r # " { "beforeStartedAt": "2021-12-03T23:45" } "# ;
let err = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap_err ( ) ;
snapshot! ( format! ( " {err} " ) , @ " Task `beforeStartedAt` `2021-12-03T23:45` is invalid. It should follow the YYYY-MM-DD or RFC 3339 date-time format. " ) ;
}
}
#[ test ]
fn deserialize_task_filter_uids ( ) {
{
let json = r # " { "uids": "78,1,12,73" } "# ;
let query = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap ( ) ;
snapshot! ( format! ( " {:?} " , query . common . uids . unwrap ( ) ) , @ " [78, 1, 12, 73] " ) ;
}
{
let json = r # " { "uids": "1" } "# ;
let query = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap ( ) ;
snapshot! ( format! ( " {:?} " , query . common . uids . unwrap ( ) ) , @ " [1] " ) ;
}
{
let json = r # " { "uids": "78,hello,world" } "# ;
let err = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap_err ( ) ;
snapshot! ( format! ( " {err} " ) , @ " Task uid `hello` is invalid. It should only contain numeric characters. " ) ;
}
{
let json = r # " { "uids": "cat" } "# ;
let err = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap_err ( ) ;
snapshot! ( format! ( " {err} " ) , @ " Task uid `cat` is invalid. It should only contain numeric characters. " ) ;
}
}
#[ test ]
fn deserialize_task_filter_status ( ) {
{
let json = r # " { "statuses": "succeeded,failed,enqueued,processing,canceled" } "# ;
let query = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap ( ) ;
snapshot! ( format! ( " {:?} " , query . common . statuses . unwrap ( ) ) , @ " [Succeeded, Failed, Enqueued, Processing, Canceled] " ) ;
}
{
let json = r # " { "statuses": "enqueued" } "# ;
let query = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap ( ) ;
snapshot! ( format! ( " {:?} " , query . common . statuses . unwrap ( ) ) , @ " [Enqueued] " ) ;
}
{
let json = r # " { "statuses": "finished" } "# ;
let err = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap_err ( ) ;
snapshot! ( format! ( " {err} " ) , @ " Task status `finished` is invalid. Available task statuses are `enqueued`, `processing`, `succeeded`, `failed`, `canceled`. " ) ;
}
}
#[ test ]
fn deserialize_task_filter_types ( ) {
{
let json = r # " { "types": "documentAdditionOrUpdate,documentDeletion,settingsUpdate,indexCreation,indexDeletion,indexUpdate,indexSwap,taskCancelation,taskDeletion,dumpCreation,snapshotCreation" }"# ;
let query = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap ( ) ;
snapshot! ( format! ( " {:?} " , query . common . types . unwrap ( ) ) , @ " [DocumentAdditionOrUpdate, DocumentDeletion, SettingsUpdate, IndexCreation, IndexDeletion, IndexUpdate, IndexSwap, TaskCancelation, TaskDeletion, DumpCreation, SnapshotCreation] " ) ;
}
{
let json = r # " { "types": "settingsUpdate" } "# ;
let query = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap ( ) ;
snapshot! ( format! ( " {:?} " , query . common . types . unwrap ( ) ) , @ " [SettingsUpdate] " ) ;
}
{
let json = r # " { "types": "createIndex" } "# ;
let err = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap_err ( ) ;
snapshot! ( format! ( " {err} " ) , @ " Task type `createIndex` is invalid. Available task types are `documentAdditionOrUpdate`, `documentDeletion`, `settingsUpdate`, `indexCreation`, `indexDeletion`, `indexUpdate`, `indexSwap`, `taskCancelation`, `taskDeletion`, `dumpCreation`, `snapshotCreation` " ) ;
}
}
#[ test ]
fn deserialize_task_filter_index_uids ( ) {
{
let json = r # " { "indexUids": "toto,tata-78" }"# ;
let query = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap ( ) ;
snapshot! ( format! ( " {:?} " , query . common . index_uids . unwrap ( ) ) , @ r ### "["toto", "tata-78"]"### ) ;
}
{
let json = r # " { "indexUids": "index_a" } "# ;
let query = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap ( ) ;
snapshot! ( format! ( " {:?} " , query . common . index_uids . unwrap ( ) ) , @ r ### "["index_a"]"### ) ;
}
{
let json = r # " { "indexUids": "1,hé" } "# ;
let err = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. 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 (_). " ) ;
}
{
let json = r # " { "indexUids": "hé" } "# ;
let err = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. 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 (_). " ) ;
}
}
#[ test ]
fn deserialize_task_filter_general ( ) {
{
let json = r # " { "from": 12, "limit": 15, "indexUids": "toto,tata-78", "statuses": "succeeded,enqueued", "afterEnqueuedAt": "2012-04-23", "uids": "1,2,3" }"# ;
let query =
serde_json ::from_str ::< TasksFilterQueryRaw > ( json ) . unwrap ( ) . validate ( ) . unwrap ( ) ;
snapshot! ( format! ( " {:?} " , query ) , @ r ### "TasksFilterQuery { limit: 15, from: Some(12), common: TaskCommonQuery { types: None, uids: Some([1, 2, 3]), canceled_by: None, statuses: Some([Succeeded, Enqueued]), index_uids: Some(["toto", "tata-78"]) }, dates: TaskDateQuery { 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 } }"### ) ;
}
{
// Stars should translate to `None` in the query
// Verify value of the default limit
let json = r # " { "indexUids": "*", "statuses": "succeeded,*", "afterEnqueuedAt": "2012-04-23", "uids": "1,2,3" }"# ;
let query =
serde_json ::from_str ::< TasksFilterQueryRaw > ( json ) . unwrap ( ) . validate ( ) . unwrap ( ) ;
snapshot! ( format! ( " {:?} " , query ) , @ " TasksFilterQuery { limit: 20, from: None, common: TaskCommonQuery { types: None, uids: Some([1, 2, 3]), canceled_by: None, statuses: None, index_uids: None }, dates: TaskDateQuery { 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 } } " ) ;
}
{
// Stars should also translate to `None` in task deletion/cancelation queries
let json = r # " { "indexUids": "*", "statuses": "succeeded,*", "afterEnqueuedAt": "2012-04-23", "uids": "1,2,3" }"# ;
let query = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json )
. unwrap ( )
. validate ( )
. unwrap ( ) ;
snapshot! ( format! ( " {:?} " , query ) , @ " TaskDeletionOrCancelationQuery { common: TaskCommonQuery { types: None, uids: Some([1, 2, 3]), canceled_by: None, statuses: None, index_uids: None }, dates: TaskDateQuery { 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 } } " ) ;
}
{
// Stars in uids not allowed
let json = r # " { "uids": "*" }"# ;
let err =
serde_json ::from_str ::< TasksFilterQueryRaw > ( json ) . unwrap ( ) . validate ( ) . unwrap_err ( ) ;
snapshot! ( format! ( " {err} " ) , @ " Task uid `*` is invalid. It should only contain numeric characters. " ) ;
}
{
// From not allowed in task deletion/cancelation queries
let json = r # " { "from": 12 }"# ;
let err = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json ) . unwrap_err ( ) ;
snapshot! ( format! ( " {err} " ) , @ " unknown field `from` at line 1 column 15 " ) ;
2022-10-19 12:59:12 +02:00
}
{
2022-11-28 16:27:41 +01:00
// Limit not allowed in task deletion/cancelation queries
let json = r # " { "limit": 12 }"# ;
let err = serde_json ::from_str ::< TaskDeletionOrCancelationQueryRaw > ( json ) . unwrap_err ( ) ;
snapshot! ( format! ( " {err} " ) , @ " unknown field `limit` at line 1 column 16 " ) ;
2022-10-19 12:59:12 +02:00
}
}
}