GetTasksWithSections
Groups all tasks for a specific incident according to the task headers (titles).
python · Cortex REST API
Source
import copy from itertools import chain from CommonServerPython import * def find_start_task(tasks: Dict): for task in tasks.values(): if task.get("type") == "start": return task return DemistoException("No start task was configured") def traverse_tasks( tasks: Dict[str, Dict], current_task: Dict, results: Dict, prev_task: Dict = None, path: List[str] = None, visited: Set = None ) -> None: """ A function to traverse playbook tasks and gather all the information about the tasks in `results` Args: tasks: Tasks in the structure of the DEMISTO API current_task: The current task we traverse on results: The results Dictionary. The tasks will be stored on the path of the task prev_task: The task we we're previously on path: This is a list of the section headers in the way to the task visited: Set if the visited tasks Returns: """ if visited is None: visited = set() if path is None: path = [] if prev_task is None: prev_task = {} task_id = current_task.get("id") task_type = current_task.get("type") task_state = current_task.get("state") if task_id not in visited: visited.add(task_id) if task_type == "start" or task_type == "title": title = demisto.get(current_task, "task.name") if not title: title = "Start" new_path = path + [title] if prev_task and prev_task.get("type") == "title" else [title] elif task_type == "condition" or task_state == "WillNotBeExecuted": new_path = path else: task = assign_task_output(current_task, path) # This is for accessing the correct `path` in results to store the specific task dct = results for p in path: dct.setdefault(p, {}) dct = dct[p] dct.setdefault("tasks", []).append(task) new_path = path if current_task.get("nextTasks"): next_tasks_ids = chain(*demisto.get(current_task, "nextTasks").values()) next_tasks: List[Dict] = [tasks.get(task_id) for task_id in next_tasks_ids] # type: ignore for next_task in next_tasks: traverse_tasks(tasks, next_task, results, current_task, new_path, visited) def assign_task_output(task: Dict, path: List[str]): task_started_date = task.get("startDate") if task.get("startDate") != "0001-01-01T00:00:00Z" else "Not Started" task_completion_time = task.get("completedDate") if task.get("completedDate") != "0001-01-01T00:00:00Z" else "Not Started" due_date = task.get("dueDate") if task.get("dueDate") != "0001-01-01T00:00:00Z" else "Not Started" task_state = task.get("state") if task.get("state") else "Not Started" return assign_params( id=task.get("id"), name=demisto.get(task, "task.name"), section="/".join(path), type=task.get("type"), owner=task.get("assignee"), state=task_state, scriptId=demisto.get(task, "task.scriptId"), startDate=task_started_date, dueDate=due_date, completedDate=task_completion_time, parentPlaybookID=task.get("parentPlaybookID"), completedBy=task.get("completedBy"), ) def add_url_to_tasks(tasks: List[Dict[str, str]], workplan_url: str): tasks = copy.deepcopy(tasks) for task in tasks: task_id = task["id"] task_url = os.path.join(workplan_url, task_id) task["id"] = f"[{task_id}]({task_url})" return tasks def get_tasks(incident_id: str): urls = demisto.demistoUrls() # works in multi tenant env as well res = demisto.internalHttpRequest("GET", f"/investigation/{incident_id}/workplan") demisto.debug(f"sent GET /investigation/{incident_id}/workplan, got response={res}") if not (tasks := json.loads(res.get("body", "{}")).get("invPlaybook", {}).get("tasks", {})): raise DemistoException(f"Workplan for incident {incident_id}, has no tasks.") demisto.debug(f"got {len(tasks)} tasks") start_task = find_start_task(tasks) tasks_nested_results: Dict = {} traverse_tasks(tasks, start_task, tasks_nested_results) task_results, md = get_tasks_and_readable(tasks_nested_results, urls.get("workPlan")) return CommandResults( outputs_prefix="Tasks", outputs_key_field="id", entry_type=EntryType.NOTE, raw_response=tasks_nested_results, outputs=task_results, readable_output=md, ) def get_tasks_and_readable(tasks_nested_results: Dict[str, Dict], workplan_url: Optional[str] = None): task_results: List[Dict] = [] md_lst = [] headers = ["id", "name", "state", "completedDate"] # Go over all nested task, and fill the md table accordingly, and fill the tasks list # I assume the nested task is nested only in two levels for k1, v1 in tasks_nested_results.items(): if "tasks" in v1: tasks = v1.get("tasks") task_results.extend(tasks) # type: ignore tasks = add_url_to_tasks(tasks, workplan_url) if workplan_url else tasks # type: ignore md_lst.append(tableToMarkdown(k1, tasks, headers=headers)[1:]) # this is for making the title bigger else: md_lst.append(f"## {k1}") for k2, v2 in v1.items(): tasks = v2.get("tasks") task_results.extend(tasks) # type: ignore tasks = add_url_to_tasks(tasks, workplan_url) if workplan_url else tasks # type: ignore md_lst.append(tableToMarkdown(k2, tasks, headers=headers)) md = "\n".join(md_lst) return task_results, md def main(): try: incident_id = demisto.args().get("investigation_id") if not incident_id: incident_id = demisto.incident().get("id") return_results(get_tasks(incident_id)) except Exception as e: demisto.error(traceback.format_exc()) # print the traceback return_error(f"Failed to execute GetTasksWithSections.\nError:\n{type(e)}, {e!s}") if __name__ in ("__main__", "builtin", "builtins"): main()
README
Groups all tasks for a specific incident according to the task headers (titles).
Script Data
| Name | Description |
|---|---|
| Script Type | python3 |
| Tags | Utility |
| Cortex XSOAR Version | 6.0.0 |
Inputs
| Argument Name | Description |
|---|---|
| investigation_id | Incident ID to create the task table. |
Outputs
| Path | Description | Type |
|---|---|---|
| Tasks | The entire task object | Unknown |
| Tasks.id | Task ID | string |
| Tasks.name | Task name | string |
| Tasks.section | Task section path | string |
| Tasks.type | Task type (regular, condition, title, playbook, start) | string |
| Tasks.owner | Task owner | string |
| Tasks.state | Task state (inprogress, Completed, WillNotBeExecuted, Error, Waiting, Blocked, and empty string for not started) | string |
| Tasks.scriptId | Task related script (empty if manual) | string |
| Tasks.startDate | Task start date | unknown |
| Tasks.completedDate | Task completed date | unknown |
| Tasks.dueDate | Task due date (SLA) | unknown |
| Tasks.parentPlaybookID | Task parent playbook ID (if the task is part of a sub-playbook) | unknown |
| Tasks.completedBy | Name of the user who completed the task | string |