redis_cloud/
tasks.rs

1//! Asynchronous task tracking and management
2//!
3//! This module provides functionality for tracking long-running operations in
4//! Redis Cloud. Many API operations are asynchronous and return a task ID that
5//! can be used to monitor progress and completion status.
6//!
7//! # Overview
8//!
9//! Redis Cloud uses tasks for operations that may take time to complete, such as:
10//! - Creating or deleting subscriptions
11//! - Database creation, updates, and deletion
12//! - Backup and restore operations
13//! - Import/export operations
14//! - Network configuration changes
15//!
16//! # Task Lifecycle
17//!
18//! 1. **Initiated**: Task is created and queued
19//! 2. **Processing**: Task is being executed
20//! 3. **Completed**: Task finished successfully
21//! 4. **Failed**: Task encountered an error
22//!
23//! # Key Features
24//!
25//! - **Task Status**: Check current status of any task
26//! - **Progress Tracking**: Monitor completion percentage for long operations
27//! - **Result Retrieval**: Get operation results once completed
28//! - **Error Information**: Access detailed error messages for failed tasks
29//! - **Task History**: Query historical task information
30//!
31//! # Example Usage
32//!
33//! ```no_run
34//! use redis_cloud::{CloudClient, TaskHandler};
35//!
36//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
37//! let client = CloudClient::builder()
38//!     .api_key("your-api-key")
39//!     .api_secret("your-api-secret")
40//!     .build()?;
41//!
42//! let handler = TaskHandler::new(client);
43//!
44//! // Get task status
45//! let task = handler.get_task_by_id("task-123".to_string()).await?;
46//!
47//! // Check if task is complete
48//! if task.status == Some("completed".to_string()) {
49//!     println!("Task completed successfully");
50//!     if let Some(response) = task.response {
51//!         println!("Result: {:?}", response);
52//!     }
53//! }
54//! # Ok(())
55//! # }
56//! ```
57
58use crate::{CloudClient, Result};
59use serde::{Deserialize, Serialize};
60use serde_json::Value;
61use std::collections::HashMap;
62
63// ============================================================================
64// Models
65// ============================================================================
66
67/// ProcessorResponse
68///
69/// Contains the result of an asynchronous operation
70#[derive(Debug, Clone, Serialize, Deserialize)]
71#[serde(rename_all = "camelCase")]
72pub struct ProcessorResponse {
73    /// ID of the created/modified resource
74    #[serde(skip_serializing_if = "Option::is_none")]
75    pub resource_id: Option<i32>,
76
77    /// Additional resource ID (for operations creating multiple resources)
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub additional_resource_id: Option<i32>,
80
81    /// Full resource object for the created/modified resource
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub resource: Option<HashMap<String, Value>>,
84
85    /// Error message if the operation failed
86    #[serde(skip_serializing_if = "Option::is_none")]
87    pub error: Option<String>,
88
89    /// Additional information about the operation
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub additional_info: Option<String>,
92
93    /// Only for truly unknown/future API fields
94    #[serde(flatten)]
95    pub extra: Value,
96}
97
98/// TaskStateUpdate
99///
100/// Represents the state and result of an asynchronous task
101#[derive(Debug, Clone, Serialize, Deserialize)]
102#[serde(rename_all = "camelCase")]
103pub struct TaskStateUpdate {
104    /// Unique task identifier
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub task_id: Option<String>,
107
108    /// Type of command being executed (e.g., "CREATE_DATABASE", "DELETE_SUBSCRIPTION")
109    #[serde(skip_serializing_if = "Option::is_none")]
110    pub command_type: Option<String>,
111
112    /// Current task status (e.g., "processing", "completed", "failed")
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub status: Option<String>,
115
116    /// Human-readable description of the task
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub description: Option<String>,
119
120    /// Timestamp of last task update
121    #[serde(skip_serializing_if = "Option::is_none")]
122    pub timestamp: Option<String>,
123
124    /// Task completion percentage (0-100)
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub progress: Option<f64>,
127
128    /// Result data once task is completed
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub response: Option<ProcessorResponse>,
131
132    /// HATEOAS links for API navigation
133    #[serde(skip_serializing_if = "Option::is_none")]
134    pub links: Option<Vec<HashMap<String, Value>>>,
135
136    /// Only for truly unknown/future API fields
137    #[serde(flatten)]
138    pub extra: Value,
139}
140
141// ============================================================================
142// Handler
143// ============================================================================
144
145/// Handler for asynchronous task operations
146///
147/// Tracks and manages long-running operations, providing status updates,
148/// progress monitoring, and result retrieval for asynchronous API calls.
149pub struct TasksHandler {
150    client: CloudClient,
151}
152
153impl TasksHandler {
154    /// Create a new handler
155    pub fn new(client: CloudClient) -> Self {
156        Self { client }
157    }
158
159    /// Get tasks
160    /// Gets a list of all currently running tasks for this account.
161    ///
162    /// GET /tasks
163    pub async fn get_all_tasks(&self) -> Result<()> {
164        self.client.get("/tasks").await
165    }
166
167    /// Get a single task
168    /// Gets details and status of a single task by the Task ID.
169    ///
170    /// GET /tasks/{taskId}
171    pub async fn get_task_by_id(&self, task_id: String) -> Result<TaskStateUpdate> {
172        self.client.get(&format!("/tasks/{}", task_id)).await
173    }
174}