Skip to content

Commit

Permalink
[GetFailedTasks] New argument - get_scripts_name (#37504)
Browse files Browse the repository at this point in the history
* [GetFailedTasks] New argument - get_scripts_name

* Add unit tests
  • Loading branch information
mmhw authored Dec 2, 2024
1 parent 05c8725 commit a7ba286
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

#### Scripts

##### GetFailedTasks

Added the *get_scripts_name* argument that enables the script to retrieve and display custom script names instead of script IDs.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def get_tenant_name():
return tenant_name


def get_failed_tasks_output(tasks: list, incident: dict):
def get_failed_tasks_output(tasks: list, incident: dict, custom_scripts_map_id_and_name: dict[str, str] = {}):
"""
Converts the failing task objects of an incident to context outputs.
Expand All @@ -59,6 +59,7 @@ def get_failed_tasks_output(tasks: list, incident: dict):

for task in tasks:
error_entries = task.get("entries", [])
command_id = task.get("task", {}).get("scriptId", '').replace('|||', '')
entry = {
"Incident ID": incident.get("id"),
"Playbook Name": task.get("ancestors", [''])[0],
Expand All @@ -67,7 +68,7 @@ def get_failed_tasks_output(tasks: list, incident: dict):
"Number of Errors": len(error_entries),
"Task ID": task.get("id"),
"Incident Created Date": incident.get("created", ''),
"Command Name": task.get("task", {}).get("scriptId", '').replace('|||', ''),
"Command Name": custom_scripts_map_id_and_name.get(command_id, command_id),
"Incident Owner": incident["owner"]
}
if task.get("task", {}).get("description"):
Expand Down Expand Up @@ -141,7 +142,49 @@ def get_incident_tasks_using_internal_request(incident: dict):
return tasks


def get_incident_data(incident: dict, rest_api_instance: str = None):
def get_custom_scripts_map_id_and_name(rest_api_instance: str | None = None) -> dict[str, str]:
uri = "automation/search"
body = {"query": "system:F"}

scripts = []
if rest_api_instance:
demisto.debug(f"Retrieving custom scripts map using REST API instance: {rest_api_instance}")
response = demisto.executeCommand(
"core-api-post",
{
"uri": uri,
"body": body,
"using": rest_api_instance,
}
)

if is_error(response):
demisto.error(f"Failed retrieving custom scripts map.\n{get_error(response)}")
else:
scripts = response[0]["Contents"]["response"].get("scripts", [])

else:
demisto.debug("Retrieving custom scripts map using internal HTTP request")
response = demisto.internalHttpRequest(
method="POST",
uri=uri,
body=body
)

if response and response.get('statusCode') == 200:
scripts = json.loads(response.get('body', '{}')).get("scripts", [])
else:
demisto.error(f'Failed running POST query to {uri}.\n{str(response)}')

custom_scripts_map_id_and_name = {
script["id"]: script["name"]
for script in scripts
}
demisto.debug(f"Retrieve the following map: {custom_scripts_map_id_and_name}")
return custom_scripts_map_id_and_name


def get_incident_data(incident: dict, rest_api_instance: str = None, get_scripts_name: bool = False):
"""
Returns the failing task objects of an incident.
The request is done using a Core REST API instance if given,
Expand All @@ -168,7 +211,11 @@ def get_incident_data(incident: dict, rest_api_instance: str = None):
'Please specify the rest_api_instance argument.')
tasks = get_incident_tasks_using_rest_api_instance(incident, rest_api_instance)

task_outputs, tasks_error_entries_number = get_failed_tasks_output(tasks, incident)
custom_scripts_map_id_and_name = {}
if get_scripts_name:
custom_scripts_map_id_and_name = get_custom_scripts_map_id_and_name(rest_api_instance)

task_outputs, tasks_error_entries_number = get_failed_tasks_output(tasks, incident, custom_scripts_map_id_and_name)
if task_outputs:
return task_outputs, tasks_error_entries_number
else:
Expand All @@ -181,6 +228,7 @@ def main():
max_incidents = arg_to_number(args.get("max_incidents")) or 300
max_incidents = min(max_incidents, 1000)
rest_api_instance = args.get("rest_api_instance")
get_scripts_name = argToBoolean(args.get("get_scripts_name", False))

number_of_failed_incidents = 0
number_of_error_entries = 0
Expand All @@ -199,7 +247,7 @@ def main():
f'Elapsed time: {time.time() - start_time}')

for incident in total_incidents:
task_outputs, incident_error_entries_num = get_incident_data(incident, rest_api_instance)
task_outputs, incident_error_entries_num = get_incident_data(incident, rest_api_instance, get_scripts_name)

if task_outputs:
incidents_output.extend(task_outputs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ args:
name: max_incidents
- description: Rest API instance to use.
name: rest_api_instance
- auto: PREDEFINED
description: Whether to replace the scripts ids to their name in case of custom scripts.
name: get_scripts_name
predefined:
- 'true'
- 'false'
comment: |-
Gets failed tasks details for incidents based on a query. Limited to 1000 incidents.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from pytest_mock import MockerFixture
import demistomock as demisto
import pytest
import json
from GetFailedTasks import main, get_failed_tasks_output, get_incident_tasks_using_internal_request, get_incident_data
from GetFailedTasks import main, get_failed_tasks_output, get_incident_tasks_using_internal_request, get_incident_data, \
get_custom_scripts_map_id_and_name
from test_data.constants import INCIDENTS_RESULT, RESTAPI_TAS_RESULT, INTERNAL_TASKS_RESULT


Expand Down Expand Up @@ -154,3 +156,68 @@ def test_get_incident_data_internal_http_request_fail(mocker):
assert internal_request_mock_res.call_count == 1
assert api_instanc_mock_res.call_count == 1
assert result[0] == []


def test_get_custom_scripts_map_id_and_name_with_rest_api(mocker: MockerFixture):
"""
Given:
A REST API instance is provided.
When:
The get_custom_scripts_map_id_and_name function is called.
Then:
It should use the core-api-post command and return the correct script map.
"""
mock_execute_command = mocker.patch.object(demisto, 'executeCommand')
mock_execute_command.return_value = [{
'Contents': {
'response': {
'scripts': [
{'id': 'script1', 'name': 'Script One'},
{'id': 'script2', 'name': 'Script Two'}
]
}
},
"Type": 1
}]

result = get_custom_scripts_map_id_and_name('rest_api_instance')

assert result == {'script1': 'Script One', 'script2': 'Script Two'}
mock_execute_command.assert_called_once_with(
'core-api-post',
{
'uri': 'automation/search',
'body': {'query': 'system:F'},
'using': 'rest_api_instance'
}
)


def test_get_custom_scripts_map_id_and_name_without_rest_api(mocker):
"""
Given:
No REST API instance is provided.
When:
The get_custom_scripts_map_id_and_name function is called.
Then:
It should use the internalHttpRequest and return the correct script map.
"""
mock_internal_request = mocker.patch('GetFailedTasks.demisto.internalHttpRequest')
mock_internal_request.return_value = {
'statusCode': 200,
'body': json.dumps({
'scripts': [
{'id': 'script3', 'name': 'Script Three'},
{'id': 'script4', 'name': 'Script Four'}
]
})
}

result = get_custom_scripts_map_id_and_name()

assert result == {'script3': 'Script Three', 'script4': 'Script Four'}
mock_internal_request.assert_called_once_with(
method='POST',
uri='automation/search',
body={'query': 'system:F'}
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "Integrations & Incidents Health Check",
"description": "Do you know which of your integrations or open incidents failed? With this content, you can view your failed integrations and open incidents",
"support": "xsoar",
"currentVersion": "1.3.22",
"currentVersion": "1.3.23",
"author": "Cortex XSOAR",
"url": "https://www.paloaltonetworks.com/cortex",
"email": "",
Expand Down

0 comments on commit a7ba286

Please sign in to comment.