awa_ui/handlers/
batch_ops.rs1use awa_model::batch_operations::{
2 self, BatchOperation, BatchOperationFilter, BatchOperationKind, BatchOperationPreview,
3 BatchOperationSpec, BatchOperationState, ListBatchOperationsFilter, SubmitBatchOperation,
4};
5use axum::extract::{Path, Query, State};
6use axum::Json;
7use serde::Deserialize;
8use uuid::Uuid;
9
10use crate::error::ApiError;
11use crate::state::AppState;
12
13#[derive(Debug, Deserialize)]
14pub struct BatchOperationPayload {
15 pub op_kind: BatchOperationKind,
16 pub filter: BatchOperationFilter,
17 pub spec: serde_json::Value,
18 #[serde(default)]
19 pub submitted_by: Option<String>,
20 #[serde(default)]
21 pub all: bool,
22}
23
24impl BatchOperationPayload {
25 fn decode_spec(
26 self,
27 ) -> Result<
28 (
29 BatchOperationSpec,
30 BatchOperationFilter,
31 Option<String>,
32 bool,
33 ),
34 ApiError,
35 > {
36 let spec = match self.op_kind {
37 BatchOperationKind::SetPriority => {
38 #[derive(Deserialize)]
39 struct SetPrioritySpec {
40 priority: i16,
41 }
42 let spec: SetPrioritySpec = serde_json::from_value(self.spec)?;
43 BatchOperationSpec::SetPriority {
44 priority: spec.priority,
45 }
46 }
47 BatchOperationKind::MoveQueue => {
48 #[derive(Deserialize)]
49 struct MoveQueueSpec {
50 queue: String,
51 priority: Option<i16>,
52 }
53 let spec: MoveQueueSpec = serde_json::from_value(self.spec)?;
54 BatchOperationSpec::MoveQueue {
55 queue: spec.queue,
56 priority: spec.priority,
57 }
58 }
59 };
60 Ok((spec, self.filter, self.submitted_by, self.all))
61 }
62}
63
64#[derive(Debug, Deserialize)]
65pub struct ListBatchOperationsParams {
66 pub state: Option<BatchOperationState>,
67 pub limit: Option<i64>,
68}
69
70#[derive(Debug, Deserialize)]
71pub struct PatchBatchOperationPayload {
72 pub state: BatchOperationState,
73}
74
75pub async fn preview_batch_operation(
76 State(state): State<AppState>,
77 Json(payload): Json<BatchOperationPayload>,
78) -> Result<Json<BatchOperationPreview>, ApiError> {
79 state.require_writable()?;
80 let (spec, filter, _, _) = payload.decode_spec()?;
81 let preview = batch_operations::preview_batch_operation(&state.pool, spec, filter).await?;
82 Ok(Json(preview))
83}
84
85pub async fn submit_batch_operation(
86 State(state): State<AppState>,
87 Json(payload): Json<BatchOperationPayload>,
88) -> Result<Json<BatchOperation>, ApiError> {
89 state.require_writable()?;
90 let (spec, filter, submitted_by, all) = payload.decode_spec()?;
91 let operation = batch_operations::submit_batch_operation(
92 &state.pool,
93 SubmitBatchOperation {
94 spec,
95 filter,
96 submitted_by,
97 allow_all: all,
98 },
99 )
100 .await?;
101 state.invalidate_dashboard_caches();
102 Ok(Json(operation))
103}
104
105pub async fn list_batch_operations(
106 State(state): State<AppState>,
107 Query(params): Query<ListBatchOperationsParams>,
108) -> Result<Json<Vec<BatchOperation>>, ApiError> {
109 let operations = batch_operations::list_batch_operations(
110 &state.pool,
111 &ListBatchOperationsFilter {
112 state: params.state,
113 limit: params.limit,
114 },
115 )
116 .await?;
117 Ok(Json(operations))
118}
119
120pub async fn get_batch_operation(
121 State(state): State<AppState>,
122 Path(id): Path<Uuid>,
123) -> Result<Json<BatchOperation>, ApiError> {
124 Ok(Json(
125 batch_operations::get_batch_operation(&state.pool, id).await?,
126 ))
127}
128
129pub async fn patch_batch_operation(
130 State(state): State<AppState>,
131 Path(id): Path<Uuid>,
132 Json(payload): Json<PatchBatchOperationPayload>,
133) -> Result<Json<BatchOperation>, ApiError> {
134 state.require_writable()?;
135 match payload.state {
136 BatchOperationState::Cancelling => Ok(Json(
137 batch_operations::request_batch_operation_cancellation(&state.pool, id).await?,
138 )),
139 _ => Err(awa_model::AwaError::Validation(
140 "only state=cancelling is supported for batch operation PATCH".to_string(),
141 )
142 .into()),
143 }
144}