Cancel a task (skip downstream execution, remove from TaskRegistry)
(
State(state): State<Arc<AppState>>,
Path((token, execution_id)): Path<(String, String)>,
)
| 213 | |
| 214 | /// Cancel a task (skip downstream execution, remove from TaskRegistry) |
| 215 | pub async fn cancel_task( |
| 216 | State(state): State<Arc<AppState>>, |
| 217 | Path((token, execution_id)): Path<(String, String)>, |
| 218 | ) -> impl IntoResponse { |
| 219 | let pool = &state.db_pool; |
| 220 | |
| 221 | let user_id = match extension_tokens::validate_token(pool, &token).await { |
| 222 | Some(uid) => uid, |
| 223 | None => { |
| 224 | return ( |
| 225 | StatusCode::UNAUTHORIZED, |
| 226 | Json(serde_json::json!({ "error": "Invalid token" })), |
| 227 | ).into_response(); |
| 228 | } |
| 229 | }; |
| 230 | |
| 231 | tracing::info!( |
| 232 | "Cancelling task {} by user {} via extension", |
| 233 | execution_id, user_id |
| 234 | ); |
| 235 | |
| 236 | // execution_id here is the full callback ID: "{projectUuid}-{nodeId}-{pulseId}" |
| 237 | // Parse out the project UUID (first 36 chars), nodeId, and pulseId (last 36 chars). |
| 238 | let callback_id = &execution_id; |
| 239 | let client = reqwest::Client::new(); |
| 240 | |
| 241 | // Parse callback ID: "{projectUuid}-{nodeId}-{pulseUuid}-{seq}" |
| 242 | let parsed = (|| { |
| 243 | let uuid_end = callback_id.splitn(6, '-').take(5).map(|s| s.len()).sum::<usize>() + 4; |
| 244 | if uuid_end >= callback_id.len() { return None; } |
| 245 | let project_id = &callback_id[..uuid_end]; |
| 246 | let remainder = &callback_id[uuid_end + 1..]; // "{nodeId}-{pulseUuid}-{seq}" |
| 247 | // Strip trailing "-{seq}" (sequence number) |
| 248 | let last_dash = remainder.rfind('-')?; |
| 249 | let without_seq = &remainder[..last_dash]; // "{nodeId}-{pulseUuid}" |
| 250 | if without_seq.len() < 37 { return None; } // at least 36 (uuid) + 1 (dash) |
| 251 | let pulse_id = &without_seq[without_seq.len() - 36..]; |
| 252 | let node_id = &without_seq[..without_seq.len() - 37]; // strip "-{pulseUuid}" |
| 253 | Some((project_id.to_string(), node_id.to_string(), pulse_id.to_string())) |
| 254 | })(); |
| 255 | |
| 256 | // If the callback ID can't be parsed (old/malformed task), just remove from TaskRegistry |
| 257 | let Some((project_id, node_id, pulse_id)) = parsed else { |
| 258 | tracing::info!("Unparseable callback ID '{}', removing stale task from TaskRegistry", callback_id); |
| 259 | let restate_url = format!( |
| 260 | "{}/TaskRegistry/global/complete_task", |
| 261 | state.restate_url |
| 262 | ); |
| 263 | let _ = client.post(&restate_url).json(&callback_id).send().await; |
| 264 | return Json(serde_json::json!({ "status": "removed" })).into_response(); |
| 265 | }; |
| 266 | |
| 267 | // Call provide_input with skip=true |
| 268 | let executor_url = format!( |
| 269 | "{}/ProjectExecutor/{}/provide_input", |
| 270 | state.executor_url, project_id |
| 271 | ); |
| 272 |
nothing calls this directly
no test coverage detected