github.com/wfusion/gofusion@v1.1.14/common/infra/asynq/asynqmon/task_handlers.go (about) 1 package asynqmon 2 3 import ( 4 "errors" 5 "log" 6 "net/http" 7 "strconv" 8 "strings" 9 "time" 10 11 "github.com/gorilla/mux" 12 13 "github.com/wfusion/gofusion/common/infra/asynq" 14 "github.com/wfusion/gofusion/common/utils/serialize/json" 15 ) 16 17 // **************************************************************************** 18 // This file defines: 19 // - http.Handler(s) for task related endpoints 20 // **************************************************************************** 21 22 type listActiveTasksResponse struct { 23 Tasks []*activeTask `json:"tasks"` 24 Stats *queueStateSnapshot `json:"stats"` 25 } 26 27 func newListActiveTasksHandlerFunc(inspector *asynq.Inspector, pf PayloadFormatter) http.HandlerFunc { 28 return func(w http.ResponseWriter, r *http.Request) { 29 vars := mux.Vars(r) 30 qname := vars["qname"] 31 pageSize, pageNum := getPageOptions(r) 32 33 tasks, err := inspector.ListActiveTasks( 34 qname, asynq.PageSize(pageSize), asynq.Page(pageNum)) 35 if err != nil { 36 http.Error(w, err.Error(), http.StatusInternalServerError) 37 return 38 } 39 qinfo, err := inspector.GetQueueInfo(qname) 40 if err != nil { 41 http.Error(w, err.Error(), http.StatusInternalServerError) 42 return 43 } 44 servers, err := inspector.Servers() 45 if err != nil { 46 http.Error(w, err.Error(), http.StatusInternalServerError) 47 return 48 } 49 // m maps taskID to workerInfo. 50 m := make(map[string]*asynq.WorkerInfo) 51 for _, srv := range servers { 52 for _, w := range srv.ActiveWorkers { 53 if w.Queue == qname { 54 m[w.TaskID] = w 55 } 56 } 57 } 58 activeTasks := toActiveTasks(tasks, pf) 59 for _, t := range activeTasks { 60 workerInfo, ok := m[t.ID] 61 if ok { 62 t.Started = workerInfo.Started.Format(time.RFC3339) 63 t.Deadline = workerInfo.Deadline.Format(time.RFC3339) 64 } else { 65 t.Started = "-" 66 t.Deadline = "-" 67 } 68 } 69 70 resp := listActiveTasksResponse{ 71 Tasks: activeTasks, 72 Stats: toQueueStateSnapshot(qinfo), 73 } 74 writeResponseJSON(w, resp) 75 } 76 } 77 78 func newCancelActiveTaskHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 79 return func(w http.ResponseWriter, r *http.Request) { 80 id := mux.Vars(r)["task_id"] 81 if err := inspector.CancelProcessing(id); err != nil { 82 http.Error(w, err.Error(), http.StatusInternalServerError) 83 return 84 } 85 w.WriteHeader(http.StatusNoContent) 86 } 87 } 88 89 func newCancelAllActiveTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 90 return func(w http.ResponseWriter, r *http.Request) { 91 const batchSize = 100 92 page := 1 93 qname := mux.Vars(r)["qname"] 94 for { 95 tasks, err := inspector.ListActiveTasks(qname, asynq.Page(page), asynq.PageSize(batchSize)) 96 if err != nil { 97 http.Error(w, err.Error(), http.StatusInternalServerError) 98 return 99 } 100 for _, t := range tasks { 101 if err := inspector.CancelProcessing(t.ID); err != nil { 102 http.Error(w, err.Error(), http.StatusInternalServerError) 103 return 104 } 105 } 106 if len(tasks) < batchSize { 107 break 108 } 109 page++ 110 } 111 w.WriteHeader(http.StatusNoContent) 112 } 113 } 114 115 type batchCancelTasksRequest struct { 116 TaskIDs []string `json:"task_ids"` 117 } 118 119 type batchCancelTasksResponse struct { 120 CanceledIDs []string `json:"canceled_ids"` 121 ErrorIDs []string `json:"error_ids"` 122 } 123 124 func newBatchCancelActiveTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 125 return func(w http.ResponseWriter, r *http.Request) { 126 r.Body = http.MaxBytesReader(w, r.Body, maxRequestBodySize) 127 dec := json.NewDecoder(r.Body) 128 dec.DisallowUnknownFields() 129 130 var req batchCancelTasksRequest 131 if err := dec.Decode(&req); err != nil { 132 http.Error(w, err.Error(), http.StatusBadRequest) 133 return 134 } 135 136 resp := batchCancelTasksResponse{ 137 // avoid null in the json response 138 CanceledIDs: make([]string, 0), 139 ErrorIDs: make([]string, 0), 140 } 141 for _, id := range req.TaskIDs { 142 if err := inspector.CancelProcessing(id); err != nil { 143 log.Printf("error: could not send cancelation signal to task %s", id) 144 resp.ErrorIDs = append(resp.ErrorIDs, id) 145 } else { 146 resp.CanceledIDs = append(resp.CanceledIDs, id) 147 } 148 } 149 writeResponseJSON(w, resp) 150 } 151 } 152 153 func newListPendingTasksHandlerFunc(inspector *asynq.Inspector, pf PayloadFormatter) http.HandlerFunc { 154 return func(w http.ResponseWriter, r *http.Request) { 155 vars := mux.Vars(r) 156 qname := vars["qname"] 157 pageSize, pageNum := getPageOptions(r) 158 tasks, err := inspector.ListPendingTasks( 159 qname, asynq.PageSize(pageSize), asynq.Page(pageNum)) 160 if err != nil { 161 http.Error(w, err.Error(), http.StatusInternalServerError) 162 return 163 } 164 qinfo, err := inspector.GetQueueInfo(qname) 165 if err != nil { 166 http.Error(w, err.Error(), http.StatusInternalServerError) 167 return 168 } 169 payload := make(map[string]any) 170 if len(tasks) == 0 { 171 // avoid nil for the tasks field in json output. 172 payload["tasks"] = make([]*pendingTask, 0) 173 } else { 174 payload["tasks"] = toPendingTasks(tasks, pf) 175 } 176 payload["stats"] = toQueueStateSnapshot(qinfo) 177 writeResponseJSON(w, payload) 178 } 179 } 180 181 func newListScheduledTasksHandlerFunc(inspector *asynq.Inspector, pf PayloadFormatter) http.HandlerFunc { 182 return func(w http.ResponseWriter, r *http.Request) { 183 vars := mux.Vars(r) 184 qname := vars["qname"] 185 pageSize, pageNum := getPageOptions(r) 186 tasks, err := inspector.ListScheduledTasks( 187 qname, asynq.PageSize(pageSize), asynq.Page(pageNum)) 188 if err != nil { 189 http.Error(w, err.Error(), http.StatusInternalServerError) 190 return 191 } 192 qinfo, err := inspector.GetQueueInfo(qname) 193 if err != nil { 194 http.Error(w, err.Error(), http.StatusInternalServerError) 195 return 196 } 197 payload := make(map[string]any) 198 if len(tasks) == 0 { 199 // avoid nil for the tasks field in json output. 200 payload["tasks"] = make([]*scheduledTask, 0) 201 } else { 202 payload["tasks"] = toScheduledTasks(tasks, pf) 203 } 204 payload["stats"] = toQueueStateSnapshot(qinfo) 205 writeResponseJSON(w, payload) 206 } 207 } 208 209 func newListRetryTasksHandlerFunc(inspector *asynq.Inspector, pf PayloadFormatter) http.HandlerFunc { 210 return func(w http.ResponseWriter, r *http.Request) { 211 vars := mux.Vars(r) 212 qname := vars["qname"] 213 pageSize, pageNum := getPageOptions(r) 214 tasks, err := inspector.ListRetryTasks( 215 qname, asynq.PageSize(pageSize), asynq.Page(pageNum)) 216 if err != nil { 217 http.Error(w, err.Error(), http.StatusInternalServerError) 218 return 219 } 220 qinfo, err := inspector.GetQueueInfo(qname) 221 if err != nil { 222 http.Error(w, err.Error(), http.StatusInternalServerError) 223 return 224 } 225 payload := make(map[string]any) 226 if len(tasks) == 0 { 227 // avoid nil for the tasks field in json output. 228 payload["tasks"] = make([]*retryTask, 0) 229 } else { 230 payload["tasks"] = toRetryTasks(tasks, pf) 231 } 232 payload["stats"] = toQueueStateSnapshot(qinfo) 233 writeResponseJSON(w, payload) 234 } 235 } 236 237 func newListArchivedTasksHandlerFunc(inspector *asynq.Inspector, pf PayloadFormatter) http.HandlerFunc { 238 return func(w http.ResponseWriter, r *http.Request) { 239 vars := mux.Vars(r) 240 qname := vars["qname"] 241 pageSize, pageNum := getPageOptions(r) 242 tasks, err := inspector.ListArchivedTasks( 243 qname, asynq.PageSize(pageSize), asynq.Page(pageNum)) 244 if err != nil { 245 http.Error(w, err.Error(), http.StatusInternalServerError) 246 return 247 } 248 qinfo, err := inspector.GetQueueInfo(qname) 249 if err != nil { 250 http.Error(w, err.Error(), http.StatusInternalServerError) 251 return 252 } 253 payload := make(map[string]any) 254 if len(tasks) == 0 { 255 // avoid nil for the tasks field in json output. 256 payload["tasks"] = make([]*archivedTask, 0) 257 } else { 258 payload["tasks"] = toArchivedTasks(tasks, pf) 259 } 260 payload["stats"] = toQueueStateSnapshot(qinfo) 261 writeResponseJSON(w, payload) 262 } 263 } 264 265 func newListCompletedTasksHandlerFunc(inspector *asynq.Inspector, pf PayloadFormatter, rf ResultFormatter) http.HandlerFunc { 266 return func(w http.ResponseWriter, r *http.Request) { 267 vars := mux.Vars(r) 268 qname := vars["qname"] 269 pageSize, pageNum := getPageOptions(r) 270 tasks, err := inspector.ListCompletedTasks(qname, asynq.PageSize(pageSize), asynq.Page(pageNum)) 271 if err != nil { 272 http.Error(w, err.Error(), http.StatusInternalServerError) 273 return 274 } 275 qinfo, err := inspector.GetQueueInfo(qname) 276 if err != nil { 277 http.Error(w, err.Error(), http.StatusInternalServerError) 278 return 279 } 280 payload := make(map[string]any) 281 if len(tasks) == 0 { 282 // avoid nil for the tasks field in json output. 283 payload["tasks"] = make([]*completedTask, 0) 284 } else { 285 payload["tasks"] = toCompletedTasks(tasks, pf, rf) 286 } 287 payload["stats"] = toQueueStateSnapshot(qinfo) 288 writeResponseJSON(w, payload) 289 } 290 } 291 292 func newListAggregatingTasksHandlerFunc(inspector *asynq.Inspector, pf PayloadFormatter) http.HandlerFunc { 293 return func(w http.ResponseWriter, r *http.Request) { 294 vars := mux.Vars(r) 295 qname := vars["qname"] 296 gname := vars["gname"] 297 pageSize, pageNum := getPageOptions(r) 298 tasks, err := inspector.ListAggregatingTasks( 299 qname, gname, asynq.PageSize(pageSize), asynq.Page(pageNum)) 300 if err != nil { 301 http.Error(w, err.Error(), http.StatusInternalServerError) 302 return 303 } 304 qinfo, err := inspector.GetQueueInfo(qname) 305 if err != nil { 306 http.Error(w, err.Error(), http.StatusInternalServerError) 307 return 308 } 309 groups, err := inspector.Groups(qname) 310 if err != nil { 311 http.Error(w, err.Error(), http.StatusInternalServerError) 312 return 313 } 314 payload := make(map[string]any) 315 if len(tasks) == 0 { 316 // avoid nil for the tasks field in json output. 317 payload["tasks"] = make([]*aggregatingTask, 0) 318 } else { 319 payload["tasks"] = toAggregatingTasks(tasks, pf) 320 } 321 payload["stats"] = toQueueStateSnapshot(qinfo) 322 payload["groups"] = toGroupInfos(groups) 323 writeResponseJSON(w, payload) 324 } 325 } 326 327 func newDeleteTaskHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 328 return func(w http.ResponseWriter, r *http.Request) { 329 vars := mux.Vars(r) 330 qname, taskid := vars["qname"], vars["task_id"] 331 if qname == "" || taskid == "" { 332 http.Error(w, "route parameters should not be empty", http.StatusBadRequest) 333 return 334 } 335 if err := inspector.DeleteTask(qname, taskid); err != nil { 336 // TODO: Handle task not found error and return 404 337 http.Error(w, err.Error(), http.StatusInternalServerError) 338 return 339 } 340 w.WriteHeader(http.StatusNoContent) 341 } 342 } 343 344 func newRunTaskHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 345 return func(w http.ResponseWriter, r *http.Request) { 346 vars := mux.Vars(r) 347 qname, taskid := vars["qname"], vars["task_id"] 348 if qname == "" || taskid == "" { 349 http.Error(w, "route parameters should not be empty", http.StatusBadRequest) 350 return 351 } 352 if err := inspector.RunTask(qname, taskid); err != nil { 353 // TODO: Handle task not found error and return 404 354 http.Error(w, err.Error(), http.StatusInternalServerError) 355 return 356 } 357 w.WriteHeader(http.StatusNoContent) 358 } 359 } 360 361 func newArchiveTaskHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 362 return func(w http.ResponseWriter, r *http.Request) { 363 vars := mux.Vars(r) 364 qname, taskid := vars["qname"], vars["task_id"] 365 if qname == "" || taskid == "" { 366 http.Error(w, "route parameters should not be empty", http.StatusBadRequest) 367 return 368 } 369 if err := inspector.ArchiveTask(qname, taskid); err != nil { 370 // TODO: Handle task not found error and return 404 371 http.Error(w, err.Error(), http.StatusInternalServerError) 372 return 373 } 374 w.WriteHeader(http.StatusNoContent) 375 } 376 } 377 378 type deleteAllTasksResponse struct { 379 // Number of tasks deleted. 380 Deleted int `json:"deleted"` 381 } 382 383 func newDeleteAllPendingTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 384 return func(w http.ResponseWriter, r *http.Request) { 385 qname := mux.Vars(r)["qname"] 386 n, err := inspector.DeleteAllPendingTasks(qname) 387 if err != nil { 388 http.Error(w, err.Error(), http.StatusInternalServerError) 389 return 390 } 391 writeResponseJSON(w, deleteAllTasksResponse{n}) 392 } 393 } 394 395 func newDeleteAllAggregatingTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 396 return func(w http.ResponseWriter, r *http.Request) { 397 vars := mux.Vars(r) 398 qname, gname := vars["qname"], vars["gname"] 399 n, err := inspector.DeleteAllAggregatingTasks(qname, gname) 400 if err != nil { 401 http.Error(w, err.Error(), http.StatusInternalServerError) 402 return 403 } 404 writeResponseJSON(w, deleteAllTasksResponse{n}) 405 } 406 } 407 408 func newDeleteAllScheduledTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 409 return func(w http.ResponseWriter, r *http.Request) { 410 qname := mux.Vars(r)["qname"] 411 n, err := inspector.DeleteAllScheduledTasks(qname) 412 if err != nil { 413 http.Error(w, err.Error(), http.StatusInternalServerError) 414 return 415 } 416 writeResponseJSON(w, deleteAllTasksResponse{n}) 417 } 418 } 419 420 func newDeleteAllRetryTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 421 return func(w http.ResponseWriter, r *http.Request) { 422 qname := mux.Vars(r)["qname"] 423 n, err := inspector.DeleteAllRetryTasks(qname) 424 if err != nil { 425 http.Error(w, err.Error(), http.StatusInternalServerError) 426 return 427 } 428 writeResponseJSON(w, deleteAllTasksResponse{n}) 429 } 430 } 431 432 func newDeleteAllArchivedTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 433 return func(w http.ResponseWriter, r *http.Request) { 434 qname := mux.Vars(r)["qname"] 435 n, err := inspector.DeleteAllArchivedTasks(qname) 436 if err != nil { 437 http.Error(w, err.Error(), http.StatusInternalServerError) 438 return 439 } 440 writeResponseJSON(w, deleteAllTasksResponse{n}) 441 } 442 } 443 444 func newDeleteAllCompletedTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 445 return func(w http.ResponseWriter, r *http.Request) { 446 qname := mux.Vars(r)["qname"] 447 n, err := inspector.DeleteAllCompletedTasks(qname) 448 if err != nil { 449 http.Error(w, err.Error(), http.StatusInternalServerError) 450 return 451 } 452 writeResponseJSON(w, deleteAllTasksResponse{n}) 453 } 454 } 455 456 type runAllTasksResponse struct { 457 // Number of tasks scheduled to run. 458 Scheduled int `json:"scheduled"` 459 } 460 461 func newRunAllScheduledTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 462 return func(w http.ResponseWriter, r *http.Request) { 463 qname := mux.Vars(r)["qname"] 464 n, err := inspector.RunAllScheduledTasks(qname) 465 if err != nil { 466 http.Error(w, err.Error(), http.StatusInternalServerError) 467 return 468 } 469 writeResponseJSON(w, runAllTasksResponse{n}) 470 } 471 } 472 473 func newRunAllRetryTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 474 return func(w http.ResponseWriter, r *http.Request) { 475 qname := mux.Vars(r)["qname"] 476 n, err := inspector.RunAllRetryTasks(qname) 477 if err != nil { 478 http.Error(w, err.Error(), http.StatusInternalServerError) 479 return 480 } 481 writeResponseJSON(w, runAllTasksResponse{n}) 482 } 483 } 484 485 func newRunAllArchivedTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 486 return func(w http.ResponseWriter, r *http.Request) { 487 qname := mux.Vars(r)["qname"] 488 n, err := inspector.RunAllArchivedTasks(qname) 489 if err != nil { 490 http.Error(w, err.Error(), http.StatusInternalServerError) 491 return 492 } 493 writeResponseJSON(w, runAllTasksResponse{n}) 494 } 495 } 496 497 func newRunAllAggregatingTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 498 return func(w http.ResponseWriter, r *http.Request) { 499 vars := mux.Vars(r) 500 qname, gname := vars["qname"], vars["gname"] 501 n, err := inspector.RunAllAggregatingTasks(qname, gname) 502 if err != nil { 503 http.Error(w, err.Error(), http.StatusInternalServerError) 504 return 505 } 506 writeResponseJSON(w, runAllTasksResponse{n}) 507 } 508 } 509 510 type archiveAllTasksResponse struct { 511 // Number of tasks archived. 512 Archived int `json:"archived"` 513 } 514 515 func writeResponseJSON(w http.ResponseWriter, resp any) { 516 if err := json.NewEncoder(w).Encode(resp); err != nil { 517 http.Error(w, err.Error(), http.StatusInternalServerError) 518 } 519 } 520 521 func newArchiveAllPendingTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 522 return func(w http.ResponseWriter, r *http.Request) { 523 qname := mux.Vars(r)["qname"] 524 n, err := inspector.ArchiveAllPendingTasks(qname) 525 if err != nil { 526 http.Error(w, err.Error(), http.StatusInternalServerError) 527 return 528 } 529 writeResponseJSON(w, archiveAllTasksResponse{n}) 530 } 531 } 532 533 func newArchiveAllAggregatingTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 534 return func(w http.ResponseWriter, r *http.Request) { 535 vars := mux.Vars(r) 536 qname, gname := vars["qname"], vars["gname"] 537 n, err := inspector.ArchiveAllAggregatingTasks(qname, gname) 538 if err != nil { 539 http.Error(w, err.Error(), http.StatusInternalServerError) 540 return 541 } 542 writeResponseJSON(w, archiveAllTasksResponse{n}) 543 } 544 } 545 546 func newArchiveAllScheduledTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 547 return func(w http.ResponseWriter, r *http.Request) { 548 qname := mux.Vars(r)["qname"] 549 n, err := inspector.ArchiveAllScheduledTasks(qname) 550 if err != nil { 551 http.Error(w, err.Error(), http.StatusInternalServerError) 552 return 553 } 554 writeResponseJSON(w, archiveAllTasksResponse{n}) 555 } 556 } 557 558 func newArchiveAllRetryTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 559 return func(w http.ResponseWriter, r *http.Request) { 560 qname := mux.Vars(r)["qname"] 561 n, err := inspector.ArchiveAllRetryTasks(qname) 562 if err != nil { 563 http.Error(w, err.Error(), http.StatusInternalServerError) 564 return 565 } 566 writeResponseJSON(w, archiveAllTasksResponse{n}) 567 } 568 } 569 570 // request body used for all batch delete tasks endpoints. 571 type batchDeleteTasksRequest struct { 572 TaskIDs []string `json:"task_ids"` 573 } 574 575 // Note: Redis does not have any rollback mechanism, so it's possible 576 // to have partial success when doing a batch operation. 577 // For this reason this response contains a list of succeeded ids 578 // and a list of failed ids. 579 type batchDeleteTasksResponse struct { 580 // task ids that were successfully deleted. 581 DeletedIDs []string `json:"deleted_ids"` 582 583 // task ids that were not deleted. 584 FailedIDs []string `json:"failed_ids"` 585 } 586 587 // Maximum request body size in bytes. 588 // Allow up to 1MB in size. 589 const maxRequestBodySize = 1000000 590 591 func newBatchDeleteTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 592 return func(w http.ResponseWriter, r *http.Request) { 593 r.Body = http.MaxBytesReader(w, r.Body, maxRequestBodySize) 594 dec := json.NewDecoder(r.Body) 595 dec.DisallowUnknownFields() 596 597 var req batchDeleteTasksRequest 598 if err := dec.Decode(&req); err != nil { 599 http.Error(w, err.Error(), http.StatusBadRequest) 600 return 601 } 602 603 qname := mux.Vars(r)["qname"] 604 resp := batchDeleteTasksResponse{ 605 // avoid null in the json response 606 DeletedIDs: make([]string, 0), 607 FailedIDs: make([]string, 0), 608 } 609 for _, taskid := range req.TaskIDs { 610 if err := inspector.DeleteTask(qname, taskid); err != nil { 611 log.Printf("error: could not delete task with id %q: %v", taskid, err) 612 resp.FailedIDs = append(resp.FailedIDs, taskid) 613 } else { 614 resp.DeletedIDs = append(resp.DeletedIDs, taskid) 615 } 616 } 617 writeResponseJSON(w, resp) 618 } 619 } 620 621 type batchRunTasksRequest struct { 622 TaskIDs []string `json:"task_ids"` 623 } 624 625 type batchRunTasksResponse struct { 626 // task ids that were successfully moved to the pending state. 627 PendingIDs []string `json:"pending_ids"` 628 // task ids that were not able to move to the pending state. 629 ErrorIDs []string `json:"error_ids"` 630 } 631 632 func newBatchRunTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 633 return func(w http.ResponseWriter, r *http.Request) { 634 r.Body = http.MaxBytesReader(w, r.Body, maxRequestBodySize) 635 dec := json.NewDecoder(r.Body) 636 dec.DisallowUnknownFields() 637 638 var req batchRunTasksRequest 639 if err := dec.Decode(&req); err != nil { 640 http.Error(w, err.Error(), http.StatusBadRequest) 641 return 642 } 643 644 qname := mux.Vars(r)["qname"] 645 resp := batchRunTasksResponse{ 646 // avoid null in the json response 647 PendingIDs: make([]string, 0), 648 ErrorIDs: make([]string, 0), 649 } 650 for _, taskid := range req.TaskIDs { 651 if err := inspector.RunTask(qname, taskid); err != nil { 652 log.Printf("error: could not run task with id %q: %v", taskid, err) 653 resp.ErrorIDs = append(resp.ErrorIDs, taskid) 654 } else { 655 resp.PendingIDs = append(resp.PendingIDs, taskid) 656 } 657 } 658 writeResponseJSON(w, resp) 659 } 660 } 661 662 type batchArchiveTasksRequest struct { 663 TaskIDs []string `json:"task_ids"` 664 } 665 666 type batchArchiveTasksResponse struct { 667 // task ids that were successfully moved to the archived state. 668 ArchivedIDs []string `json:"archived_ids"` 669 // task ids that were not able to move to the archived state. 670 ErrorIDs []string `json:"error_ids"` 671 } 672 673 func newBatchArchiveTasksHandlerFunc(inspector *asynq.Inspector) http.HandlerFunc { 674 return func(w http.ResponseWriter, r *http.Request) { 675 r.Body = http.MaxBytesReader(w, r.Body, maxRequestBodySize) 676 dec := json.NewDecoder(r.Body) 677 dec.DisallowUnknownFields() 678 679 var req batchArchiveTasksRequest 680 if err := dec.Decode(&req); err != nil { 681 http.Error(w, err.Error(), http.StatusBadRequest) 682 return 683 } 684 685 qname := mux.Vars(r)["qname"] 686 resp := batchArchiveTasksResponse{ 687 // avoid null in the json response 688 ArchivedIDs: make([]string, 0), 689 ErrorIDs: make([]string, 0), 690 } 691 for _, taskid := range req.TaskIDs { 692 if err := inspector.ArchiveTask(qname, taskid); err != nil { 693 log.Printf("error: could not archive task with id %q: %v", taskid, err) 694 resp.ErrorIDs = append(resp.ErrorIDs, taskid) 695 } else { 696 resp.ArchivedIDs = append(resp.ArchivedIDs, taskid) 697 } 698 } 699 writeResponseJSON(w, resp) 700 } 701 } 702 703 // getPageOptions read page size and number from the request url if set, 704 // otherwise it returns the default value. 705 func getPageOptions(r *http.Request) (pageSize, pageNum int) { 706 pageSize = 20 // default page size 707 pageNum = 1 // default page num 708 q := r.URL.Query() 709 if s := q.Get("size"); s != "" { 710 if n, err := strconv.Atoi(s); err == nil { 711 pageSize = n 712 } 713 } 714 if s := q.Get("page"); s != "" { 715 if n, err := strconv.Atoi(s); err == nil { 716 pageNum = n 717 } 718 } 719 return pageSize, pageNum 720 } 721 722 func newGetTaskHandlerFunc(inspector *asynq.Inspector, pf PayloadFormatter, rf ResultFormatter) http.HandlerFunc { 723 return func(w http.ResponseWriter, r *http.Request) { 724 vars := mux.Vars(r) 725 qname, taskid := vars["qname"], vars["task_id"] 726 if qname == "" { 727 http.Error(w, "queue name cannot be empty", http.StatusBadRequest) 728 return 729 } 730 if taskid == "" { 731 http.Error(w, "task_id cannot be empty", http.StatusBadRequest) 732 return 733 } 734 735 info, err := inspector.GetTaskInfo(qname, taskid) 736 switch { 737 case errors.Is(err, asynq.ErrQueueNotFound), errors.Is(err, asynq.ErrTaskNotFound): 738 http.Error(w, strings.TrimPrefix(err.Error(), "asynq: "), http.StatusNotFound) 739 return 740 case err != nil: 741 http.Error(w, strings.TrimPrefix(err.Error(), "asynq: "), http.StatusInternalServerError) 742 return 743 } 744 745 writeResponseJSON(w, toTaskInfo(info, pf, rf)) 746 } 747 }