awa-ui 0.6.2

Web UI and JSON API for the Awa job queue
Documentation
use awa_model::batch_operations::{
    self, BatchOperation, BatchOperationFilter, BatchOperationKind, BatchOperationPreview,
    BatchOperationSpec, BatchOperationState, ListBatchOperationsFilter, SubmitBatchOperation,
};
use axum::extract::{Path, Query, State};
use axum::Json;
use serde::Deserialize;
use uuid::Uuid;

use crate::error::ApiError;
use crate::state::AppState;

#[derive(Debug, Deserialize)]
pub struct BatchOperationPayload {
    pub op_kind: BatchOperationKind,
    pub filter: BatchOperationFilter,
    pub spec: serde_json::Value,
    #[serde(default)]
    pub submitted_by: Option<String>,
    #[serde(default)]
    pub all: bool,
}

impl BatchOperationPayload {
    fn decode_spec(
        self,
    ) -> Result<
        (
            BatchOperationSpec,
            BatchOperationFilter,
            Option<String>,
            bool,
        ),
        ApiError,
    > {
        let spec = match self.op_kind {
            BatchOperationKind::SetPriority => {
                #[derive(Deserialize)]
                struct SetPrioritySpec {
                    priority: i16,
                }
                let spec: SetPrioritySpec = serde_json::from_value(self.spec)?;
                BatchOperationSpec::SetPriority {
                    priority: spec.priority,
                }
            }
            BatchOperationKind::MoveQueue => {
                #[derive(Deserialize)]
                struct MoveQueueSpec {
                    queue: String,
                    priority: Option<i16>,
                }
                let spec: MoveQueueSpec = serde_json::from_value(self.spec)?;
                BatchOperationSpec::MoveQueue {
                    queue: spec.queue,
                    priority: spec.priority,
                }
            }
        };
        Ok((spec, self.filter, self.submitted_by, self.all))
    }
}

#[derive(Debug, Deserialize)]
pub struct ListBatchOperationsParams {
    pub state: Option<BatchOperationState>,
    pub limit: Option<i64>,
}

#[derive(Debug, Deserialize)]
pub struct PatchBatchOperationPayload {
    pub state: BatchOperationState,
}

pub async fn preview_batch_operation(
    State(state): State<AppState>,
    Json(payload): Json<BatchOperationPayload>,
) -> Result<Json<BatchOperationPreview>, ApiError> {
    state.require_writable()?;
    let (spec, filter, _, _) = payload.decode_spec()?;
    let preview = batch_operations::preview_batch_operation(&state.pool, spec, filter).await?;
    Ok(Json(preview))
}

pub async fn submit_batch_operation(
    State(state): State<AppState>,
    Json(payload): Json<BatchOperationPayload>,
) -> Result<Json<BatchOperation>, ApiError> {
    state.require_writable()?;
    let (spec, filter, submitted_by, all) = payload.decode_spec()?;
    let operation = batch_operations::submit_batch_operation(
        &state.pool,
        SubmitBatchOperation {
            spec,
            filter,
            submitted_by,
            allow_all: all,
        },
    )
    .await?;
    state.invalidate_dashboard_caches();
    Ok(Json(operation))
}

pub async fn list_batch_operations(
    State(state): State<AppState>,
    Query(params): Query<ListBatchOperationsParams>,
) -> Result<Json<Vec<BatchOperation>>, ApiError> {
    let operations = batch_operations::list_batch_operations(
        &state.pool,
        &ListBatchOperationsFilter {
            state: params.state,
            limit: params.limit,
        },
    )
    .await?;
    Ok(Json(operations))
}

pub async fn get_batch_operation(
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
) -> Result<Json<BatchOperation>, ApiError> {
    Ok(Json(
        batch_operations::get_batch_operation(&state.pool, id).await?,
    ))
}

pub async fn patch_batch_operation(
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
    Json(payload): Json<PatchBatchOperationPayload>,
) -> Result<Json<BatchOperation>, ApiError> {
    state.require_writable()?;
    match payload.state {
        BatchOperationState::Cancelling => Ok(Json(
            batch_operations::request_batch_operation_cancellation(&state.pool, id).await?,
        )),
        _ => Err(awa_model::AwaError::Validation(
            "only state=cancelling is supported for batch operation PATCH".to_string(),
        )
        .into()),
    }
}