From 7ae6851949ba28aca9f9678034eaed49eae6ccd4 Mon Sep 17 00:00:00 2001 From: Shmuel Kroizer <69422117+shmuel44@users.noreply.github.com> Date: Mon, 4 Dec 2023 14:22:17 +0200 Subject: [PATCH] [PagerDuty v2] Added Support For Pagination (#30959) * commit init - lint and type annotation * typing * pagination function and some typing * fix api limit and pagination * added UT and test_data * added RN and description for args * generate readme * update docker * added UT * fix flake8 * more docstring, one more UT, fix send unnecessary parameters * fix f-string * fix pep8 * revert copy * fix parameters name * docs review * update docker --- .../Integrations/PagerDuty/PagerDuty.py | 335 +++++++++------- .../Integrations/PagerDuty/PagerDuty.yml | 278 +++++++------- .../Integrations/PagerDuty/PagerDuty_test.py | 162 ++++++-- .../Integrations/PagerDuty/README.md | 358 ++++++++++-------- .../PagerDuty/test_data/incidents.json | 254 +++++++++++++ Packs/PagerDuty/ReleaseNotes/1_1_8.md | 9 + Packs/PagerDuty/pack_metadata.json | 2 +- 7 files changed, 950 insertions(+), 448 deletions(-) create mode 100644 Packs/PagerDuty/Integrations/PagerDuty/test_data/incidents.json create mode 100644 Packs/PagerDuty/ReleaseNotes/1_1_8.md diff --git a/Packs/PagerDuty/Integrations/PagerDuty/PagerDuty.py b/Packs/PagerDuty/Integrations/PagerDuty/PagerDuty.py index 2443bd2e543a..39d62855b161 100644 --- a/Packs/PagerDuty/Integrations/PagerDuty/PagerDuty.py +++ b/Packs/PagerDuty/Integrations/PagerDuty/PagerDuty.py @@ -3,9 +3,6 @@ from CommonServerPython import * -# Disable insecure warnings -import urllib3 -urllib3.disable_warnings() ''' GLOBAL VARS ''' # PagerDuty API works only with secured communication. @@ -20,9 +17,11 @@ SERVER_URL = 'https://api.pagerduty.com/' CREATE_EVENT_URL = 'https://events.pagerduty.com/v2/enqueue' +INCIDENT_API_LIMIT = 100 + DEFAULT_HEADERS = { - 'Authorization': 'Token token=' + API_KEY, - 'Accept': 'application/vnd.pagerduty+json;version=2' + 'Authorization': f'Token token={API_KEY}', + 'Accept': 'application/vnd.pagerduty+json;version=2', } '''HANDLE PROXY''' @@ -90,8 +89,8 @@ ''' HELPER FUNCTIONS ''' -def http_request(method, url, params_dict=None, data=None, json_data=None, additional_headers=None): # pragma: no cover - LOG('running %s request with url=%s\nparams=%s' % (method, url, json.dumps(params_dict))) +def http_request(method: str, url: str, params_dict=None, data=None, json_data=None, additional_headers=None): # pragma: no cover + demisto.debug(f'running {method} request with url={url}\nparams={json.dumps(params_dict)}') headers = DEFAULT_HEADERS.copy() if not additional_headers: additional_headers = {} @@ -110,11 +109,11 @@ def http_request(method, url, params_dict=None, data=None, json_data=None, addit return unicode_to_str_recur(res.json()) except Exception as e: - LOG(e) + demisto.debug(e) raise -def translate_severity(sev): +def translate_severity(sev: str) -> int: if sev.lower() == 'high': return 3 elif sev.lower() == 'low': @@ -140,8 +139,8 @@ def test_module(): # pragma: no cover demisto.results('ok') -def extract_on_call_user_data(users, schedule_id=None): - """Extact data about user from a given schedule.""" +def extract_on_call_user_data(users: list[dict], schedule_id=None) -> CommandResults: + """Extract data about user from a given schedule.""" outputs = [] contexts = [] for user in users: @@ -177,18 +176,18 @@ def extract_on_call_user_data(users, schedule_id=None): ) -def extract_on_call_now_user_data(users_on_call_now): +def extract_on_call_now_user_data(users_on_call_now: dict[str, Any]) -> CommandResults: """Extract the user data from the oncalls json.""" - outputs = [] # type: List[Dict] - contexts = [] # type: List[Dict] - oncalls = users_on_call_now.get('oncalls', {}) + outputs: list[dict] = [] + contexts: list[dict] = [] + oncalls: list[dict] = users_on_call_now.get('oncalls', [{}]) - for i in range(len(oncalls)): + for oncall in oncalls: output = {} context = {} - data = oncalls[i] - user = data.get('user') + data = oncall + user: dict = data.get('user', {}) schedule_id = (data.get('schedule') or {}).get('id') if schedule_id: output['Schedule ID'] = schedule_id @@ -220,12 +219,12 @@ def extract_on_call_now_user_data(users_on_call_now): ) -def parse_incident_data(incidents): +def parse_incident_data(incidents) -> tuple[list, list, list]: """Parse incident data to output,context format""" outputs = [] contexts = [] raw_response = [] - for i, incident in enumerate(incidents): + for _i, incident in enumerate(incidents): output = {} context = {} @@ -268,7 +267,7 @@ def parse_incident_data(incidents): context['resolve_reason'] = output['Resolve reason'] = incident.get('resolve_reason', '') context['teams'] = [] - for team in incident.get('teams', []): + for team in incident.get('teams', [{}]): team_id = team.get('id', '') team_name = team.get('summary', '') @@ -306,8 +305,8 @@ def parse_incident_data(incidents): return outputs, contexts, raw_response -def extract_incidents_data(incidents, table_name): - """Extact data about incidents.""" +def extract_incidents_data(incidents: list[dict], table_name: str) -> dict: + """Extract data about incidents.""" outputs, contexts, _ = parse_incident_data(incidents) return { @@ -322,21 +321,22 @@ def extract_incidents_data(incidents, table_name): } -def extract_all_schedules_data(schedules): +def extract_all_schedules_data(schedules: list[dict]) -> dict: """Extract the data about all the schedules.""" outputs = [] contexts = [] - for i in range(len(schedules)): - output = {} - context = {} # type: Dict - data = schedules[i] - - output['ID'] = data.get('id') - output['Name'] = data.get('name') - output['Time Zone'] = data.get('time_zone') - output['Today'] = datetime.today().strftime('%Y-%m-%d') - escalation_policies = data.get('escalation_policies', []) - if len(escalation_policies) > 0: + for schedule in schedules: + context: dict = {} + data = schedule + + output = { + 'ID': data.get('id'), + 'Name': data.get('name'), + 'Time Zone': data.get('time_zone'), + 'Today': datetime.today().strftime('%Y-%m-%d'), + } + escalation_policies: list[dict] = data.get('escalation_policies', [{}]) + if escalation_policies: output['Escalation Policy ID'] = escalation_policies[0].get('id') output['Escalation Policy'] = escalation_policies[0].get('summary') @@ -368,7 +368,7 @@ def extract_all_schedules_data(schedules): def create_new_incident(source, summary, severity, action, description='No description', group='', - event_class='', component='', incident_key=None, service_key=SERVICE_KEY): + event_class='', component='', incident_key=None, service_key=SERVICE_KEY) -> dict: """Create a new incident in the PagerDuty instance.""" payload = { 'routing_key': service_key, @@ -392,7 +392,7 @@ def create_new_incident(source, summary, severity, action, description='No descr return http_request('POST', CREATE_EVENT_URL, data=json.dumps(payload)) -def resolve_or_ack_incident(action, incident_key, service_key=SERVICE_KEY): +def resolve_or_ack_incident(action, incident_key, service_key=SERVICE_KEY) -> dict: """Resolve or Acknowledge an incident in the PagerDuty instance.""" payload = { 'routing_key': service_key, @@ -403,19 +403,18 @@ def resolve_or_ack_incident(action, incident_key, service_key=SERVICE_KEY): return http_request('POST', CREATE_EVENT_URL, data=json.dumps(payload)) -def extract_new_event_data(table_name, response): +def extract_new_event_data(table_name: str, response: dict) -> dict: """Extract the data from the response of creating a new command.""" - output = {} - context = {} - - output['Status'] = response.get('status', '') - output['Message'] = response.get('message', '') - output['Incident key'] = response.get('dedup_key', '') - - context['Status'] = output['Status'] - context['Message'] = output['Message'] - context['incident_key'] = output['Incident key'] - + output = { + 'Status': response.get('status', ''), + 'Message': response.get('message', ''), + 'Incident key': response.get('dedup_key', ''), + } + context = { + 'Status': output['Status'], + 'Message': output['Message'], + 'incident_key': output['Incident key'], + } return { 'Type': entryTypes['note'], 'Contents': response, @@ -429,11 +428,11 @@ def extract_new_event_data(table_name, response): } -def extract_users_contact_methods(user_contact_methods): +def extract_users_contact_methods(user_contact_methods: dict) -> dict: """Extract all the contact methods of a given user.""" outputs = [] contexts = [] - contact_methods = user_contact_methods.get('contact_methods') + contact_methods: list[dict] = user_contact_methods.get('contact_methods', [{}]) for contact_method in contact_methods: output = { 'ID': contact_method.get('id'), @@ -447,7 +446,7 @@ def extract_users_contact_methods(user_contact_methods): outputs.append(output) del contact_method['address'] - if output['Type'] == 'SMS' or output['Type'] == 'Phone': + if output['Type'] in ['SMS', 'Phone']: del contact_method['country_code'] contact_method['phone'] = output['Details'] else: @@ -467,10 +466,10 @@ def extract_users_contact_methods(user_contact_methods): } -def extract_users_notification_role(user_notification_role): +def extract_users_notification_role(user_notification_role: dict) -> dict: """Extract the notification role of a given user.""" outputs = [] - notification_rules = user_notification_role.get('notification_rules') + notification_rules: list[dict] = user_notification_role.get('notification_rules', [{}]) for notification_rule in notification_rules: output = { 'ID': notification_rule.get('id'), @@ -492,15 +491,13 @@ def extract_users_notification_role(user_notification_role): } -def extract_responder_request(responder_request_response): +def extract_responder_request(responder_request_response) -> CommandResults: """Extract the users that were requested to respond""" outputs = [] responder_request = responder_request_response.get("responder_request") for request in responder_request.get("responder_request_targets", []): request = request.get("responder_request_target") - output = {} - output["Type"] = request.get("type") - output["ID"] = request.get("id") + output = {"Type": request.get("type"), "ID": request.get("id")} if output["Type"] == "user": responder_user = request.get("incidents_responders", [])[0].get("user") else: @@ -553,31 +550,102 @@ def fetch_incidents(): demisto.setLastRun({'time': now}) -def configure_status(status='triggered,acknowledged'): +def configure_status(status='triggered,acknowledged') -> str: statuses = status.split(',') - statuses_string = "&" + STATUSES + '=' + statuses_string = f"&{STATUSES}=" statuses = statuses_string.join(statuses) - status_request = '&' + STATUSES + '=' + statuses + status_request = f'&{STATUSES}={statuses}' status_request = status_request + INCLUDED_FIELDS + UTC_PARAM return status_request -def get_incidents_command(since=None, until=None, status='triggered,acknowledged', sortBy=None, incident_key=None): +def pagination_incidents(param_dict: dict, pagination_dict: dict, url: str) -> list[dict]: + """ + Retrieves incident data through paginated requests. + + Args: + param_dict (dict): A dictionary containing parameters for controlling pagination, including: + - 'page': Current page number (optional). + - 'page_size': Number of incidents per page (optional). + - 'limit': Maximum number of incidents to retrieve (optional). + - Additional parameters to include in the API request. + + url (str): The URL of the API endpoint for incident data. + + Returns: + list[dict]: A list of dictionaries, where each dictionary represents an incident. + + Notes: + This function supports pagination for efficient retrieval of large datasets. It calculates + the appropriate 'limit' and 'offset' values based on the provided arguments. + + Examples: + To retrieve incidents for the second page with a page size of 20: + >>> pagination_incidents({'page': 2, 'page_size': 20}, 'https://api.example.com/incidents', {}) + + To retrieve the first 50 incidents without explicit pagination: + >>> pagination_incidents({'limit': 50}, 'https://api.example.com/incidents', {'status': 'open'}) + """ + def _get_page(**pagination_args) -> list[dict]: + # param_dict must be before pagination_args for merging to work correctly + return http_request('GET', url, param_dict | pagination_args).get("incidents", [{}]) + + page: list = [] + + page_number = arg_to_number(pagination_dict.get("page")) + page_size = arg_to_number(pagination_dict.get("page_size")) + + if page_number is not None and page_size is not None: + if page_size > INCIDENT_API_LIMIT: + raise DemistoException(f"The max size for page is {INCIDENT_API_LIMIT}. Please provide a lower page size.") + limit = page_size + offset = (page_number - 1) * page_size + + else: + limit = arg_to_number(pagination_dict.get("limit")) or 50 + offset = 0 + + if limit > INCIDENT_API_LIMIT: + for offset in range(0, limit - INCIDENT_API_LIMIT, INCIDENT_API_LIMIT): + page += _get_page( + limit=INCIDENT_API_LIMIT, + offset=offset) + + # the remaining call can be less than OR equal the api_limit but not empty + limit = limit % INCIDENT_API_LIMIT or INCIDENT_API_LIMIT + offset += INCIDENT_API_LIMIT + + page += _get_page( + limit=limit, + offset=offset) + + return page + + +def get_incidents_command(args: dict[str, str]) -> dict: """Get incidents command.""" - param_dict = {} - if since is not None: - param_dict['since'] = since - if until is not None: - param_dict['until'] = until - if sortBy is not None: - param_dict['sortBy'] = sortBy - if incident_key: - param_dict['incident_key'] = incident_key + param_dict: dict = { + "since": args.get("since"), + "until": args.get("until"), + "sortBy": args.get("sortBy"), + "incident_key": args.get("incident_key"), + "user_ids[]": argToList(args.get("user_id")), + "urgencies[]": args.get("urgencies"), + "date_range": args.get("date_range") + } + pagination_args = { + "page": arg_to_number(args.get("page")), + "page_size": arg_to_number(args.get("page_size")), + "limit": arg_to_number(args.get("limit", 50)) + } + remove_nulls_from_dictionary(pagination_args) + remove_nulls_from_dictionary(param_dict) - url = SERVER_URL + GET_INCIDENTS_SUFFIX + configure_status(status) - res = http_request('GET', url, param_dict) - return extract_incidents_data(res.get('incidents', []), INCIDENTS_LIST) + url = SERVER_URL + GET_INCIDENTS_SUFFIX + configure_status(args.get("status", 'triggered,acknowledged')) + incidents: list[dict] = pagination_incidents(param_dict, pagination_args, url) + + return extract_incidents_data(incidents, INCIDENTS_LIST) def submit_event_command(source, summary, severity, action, description='No description', group='', @@ -592,7 +660,7 @@ def submit_event_command(source, summary, severity, action, description='No desc return extract_new_event_data(TRIGGER_EVENT, res) -def get_all_schedules_command(query=None, limit=None): +def get_all_schedules_command(query=None, limit=None) -> dict: """Get all the schedules.""" param_dict = {} if query is not None: @@ -602,11 +670,11 @@ def get_all_schedules_command(query=None, limit=None): url = SERVER_URL + GET_SCHEDULES_SUFFIX res = http_request('GET', url, param_dict) - schedules = res.get('schedules', []) + schedules = res.get('schedules', [{}]) return extract_all_schedules_data(schedules) -def get_on_call_users_command(scheduleID, since=None, until=None): +def get_on_call_users_command(scheduleID: str, since=None, until=None) -> CommandResults: """Get the list of user on call in a from scheduleID""" param_dict = {} if since is not None: @@ -616,10 +684,10 @@ def get_on_call_users_command(scheduleID, since=None, until=None): url = SERVER_URL + ON_CALL_BY_SCHEDULE_SUFFIX.format(scheduleID) users_on_call = http_request('GET', url, param_dict) - return extract_on_call_user_data(users_on_call.get('users', []), scheduleID) + return extract_on_call_user_data(users_on_call.get('users', [{}]), scheduleID) -def get_on_call_now_users_command(limit=None, escalation_policy_ids=None, schedule_ids=None): +def get_on_call_now_users_command(limit=None, escalation_policy_ids=None, schedule_ids=None) -> CommandResults: """Get the list of users that are on call now.""" param_dict = {} if limit is not None: @@ -630,25 +698,25 @@ def get_on_call_now_users_command(limit=None, escalation_policy_ids=None, schedu param_dict['schedule_ids[]'] = argToList(schedule_ids) url = SERVER_URL + ON_CALLS_USERS_SUFFIX - users_on_call_now = http_request('GET', url, param_dict) + users_on_call_now: dict = http_request('GET', url, param_dict) return extract_on_call_now_user_data(users_on_call_now) -def get_users_contact_methods_command(UserID): +def get_users_contact_methods_command(UserID: str): """Get the contact methods of a given user.""" url = SERVER_URL + USERS_CONTACT_METHODS_SUFFIX.format(UserID) user_contact_methods = http_request('GET', url, {}) return extract_users_contact_methods(user_contact_methods) -def get_users_notification_command(UserID): +def get_users_notification_command(UserID) -> dict: """Get the notification rule of a given user""" url = SERVER_URL + USERS_NOTIFICATION_RULE.format(UserID) - user_notification_role = http_request('GET', url, {}) + user_notification_role: dict = http_request('GET', url, {}) return extract_users_notification_role(user_notification_role) -def resolve_event(incident_key=None, serviceKey=SERVICE_KEY): +def resolve_event(incident_key=None, serviceKey=SERVICE_KEY) -> dict: if serviceKey is None: raise Exception('You must enter a ServiceKey at the integration ' 'parameters or in the command to process this action.') @@ -664,7 +732,7 @@ def resolve_event(incident_key=None, serviceKey=SERVICE_KEY): return extract_new_event_data(RESOLVE_EVENT, action_response) -def acknowledge_event(incident_key=None, serviceKey=SERVICE_KEY): +def acknowledge_event(incident_key=None, serviceKey=SERVICE_KEY) -> dict: if serviceKey is None: raise Exception('You must enter a ServiceKey at the integration ' 'parameters or in the command to process this action.') @@ -680,28 +748,24 @@ def acknowledge_event(incident_key=None, serviceKey=SERVICE_KEY): return extract_new_event_data(ACKNOWLEDGE_EVENT, action_response) -def get_incident_data(): - incident_id = demisto.args().get('incident_id') +def get_incident_data(args: dict): + incident_id = args['incident_id'] url = SERVER_URL + GET_INCIDENT_SUFFIX + incident_id res = http_request('GET', url, {}) return extract_incidents_data([res.get('incident', {})], INCIDENT) -def get_service_keys(): +def get_service_keys() -> dict: offset = 0 - raw_response = [] - url = SERVER_URL + GET_SERVICES_SUFFIX res = http_request('GET', url, {"offset": offset}) - raw_response.append(res) - + raw_response = [res] outputs = [] contexts = [] while res.get('services', []): - services = res.get('services', []) + services: list[dict] = res.get('services', [{}]) for service in services: - output = {} context = {'ID': service.get('id'), 'Name': service.get('name'), 'Status': service.get('status'), 'CreatedAt': service.get('created_at')} @@ -710,9 +774,12 @@ def get_service_keys(): for integration in service.get('integrations', []): integration_url = integration.get('self', '') if integration_url: - integration_data = {} integration_res = http_request('GET', integration_url, {}).get('integration', {}) - integration_data['Name'] = integration_res.get('service', {}).get('summary', '') + integration_data = { + 'Name': integration_res.get('service', {}).get( + 'summary', '' + ) + } integration_data['Key'] = integration_res.get('integration_key', '') vendor_value = integration_res.get('vendor', {}) if not vendor_value: @@ -721,11 +788,12 @@ def get_service_keys(): integration_data['Vendor'] = vendor_value.get('summary', 'Missing Vendor information') integration_list.append(integration_data) - integration_string += "Name: {}, Vendor: {}, Key: {}\n".format(integration_data['Name'], - integration_data['Vendor'], - integration_data['Key']) + integration_string += (f"Name: {integration_data['Name']}, " + f"Vendor: {integration_data['Vendor']}, " + f"Key: {integration_data['Key']}\n" + ) - output['Integration'] = integration_string + output = {'Integration': integration_string} context['Integration'] = integration_list outputs.append(output) @@ -809,10 +877,10 @@ def run_response_play(incident_id, from_email, response_play_uuid): } response = http_request('POST', url, json_data=body, additional_headers={"From": from_email}) if response != {"status": "ok"}: - raise Exception("Status NOT Ok - {}".format(response)) + raise Exception(f"Status NOT Ok - {response}") return CommandResults( - readable_output="Response play successfully run to the incident " + incident_id + " by " + from_email, - raw_response=response + readable_output=f"Response play successfully run to the incident {incident_id} by {from_email}", + raw_response=response, ) @@ -820,40 +888,45 @@ def run_response_play(incident_id, from_email, response_play_uuid): def main(): + command = demisto.command() + args = demisto.args() + if not API_KEY: raise DemistoException('API key must be provided.') - LOG('command is %s' % (demisto.command(),)) + demisto.debug(f'command is {command}') try: - if demisto.command() == 'test-module': + if command == 'test-module': test_module() - elif demisto.command() == 'fetch-incidents': + elif command == 'fetch-incidents': fetch_incidents() - elif demisto.command() == 'PagerDuty-incidents': - demisto.results(get_incidents_command(**demisto.args())) - elif demisto.command() == 'PagerDuty-submit-event': - demisto.results(submit_event_command(**demisto.args())) - elif demisto.command() == 'PagerDuty-get-users-on-call': - return_results(get_on_call_users_command(**demisto.args())) - elif demisto.command() == 'PagerDuty-get-all-schedules': - demisto.results(get_all_schedules_command(**demisto.args())) - elif demisto.command() == 'PagerDuty-get-users-on-call-now': - return_results(get_on_call_now_users_command(**demisto.args())) - elif demisto.command() == 'PagerDuty-get-contact-methods': - demisto.results(get_users_contact_methods_command(**demisto.args())) - elif demisto.command() == 'PagerDuty-get-users-notification': - demisto.results(get_users_notification_command(**demisto.args())) - elif demisto.command() == 'PagerDuty-resolve-event': - demisto.results(resolve_event(**demisto.args())) - elif demisto.command() == 'PagerDuty-acknowledge-event': - demisto.results(acknowledge_event(**demisto.args())) - elif demisto.command() == 'PagerDuty-get-incident-data': - demisto.results(get_incident_data()) - elif demisto.command() == 'PagerDuty-get-service-keys': + elif command == 'PagerDuty-incidents': + demisto.results(get_incidents_command(args)) + elif command == 'PagerDuty-submit-event': + demisto.results(submit_event_command(**args)) + elif command == 'PagerDuty-get-users-on-call': + return_results(get_on_call_users_command(**args)) + elif command == 'PagerDuty-get-all-schedules': + demisto.results(get_all_schedules_command(**args)) + elif command == 'PagerDuty-get-users-on-call-now': + return_results(get_on_call_now_users_command(**args)) + elif command == 'PagerDuty-get-contact-methods': + demisto.results(get_users_contact_methods_command(**args)) + elif command == 'PagerDuty-get-users-notification': + demisto.results(get_users_notification_command(**args)) + elif command == 'PagerDuty-resolve-event': + demisto.results(resolve_event(**args)) + elif command == 'PagerDuty-acknowledge-event': + demisto.results(acknowledge_event(**args)) + elif command == 'PagerDuty-get-incident-data': + demisto.results(get_incident_data(args)) + elif command == 'PagerDuty-get-service-keys': demisto.results(get_service_keys()) - elif demisto.command() == 'PagerDuty-add-responders': - return_results(add_responders_to_incident(**demisto.args())) - elif demisto.command() == 'PagerDuty-run-response-play': - return_results(run_response_play(**demisto.args())) + elif command == 'PagerDuty-add-responders': + return_results(add_responders_to_incident(**args)) + elif command == 'PagerDuty-run-response-play': + return_results(run_response_play(**args)) + else: + raise NotImplementedError(f"Command {command} is not implemented") except Exception as err: return_error(str(err)) diff --git a/Packs/PagerDuty/Integrations/PagerDuty/PagerDuty.yml b/Packs/PagerDuty/Integrations/PagerDuty/PagerDuty.yml index 0df0d804e21e..f5e2fa6fb4ea 100644 --- a/Packs/PagerDuty/Integrations/PagerDuty/PagerDuty.yml +++ b/Packs/PagerDuty/Integrations/PagerDuty/PagerDuty.yml @@ -34,7 +34,7 @@ configuration: type: 13 required: false - defaultvalue: '1' - display: Initial Fetch Interval(In minutes, used only for first fetch or after Reset last run) + display: Initial Fetch Interval (In minutes, used only for first fetch or after Reset last run) name: FetchInterval type: 0 required: false @@ -42,57 +42,57 @@ configuration: name: DefaultRequestor type: 0 required: false -description: Alert and notify users using PagerDuty +description: Alert and notify users using PagerDuty. display: PagerDuty v2 name: PagerDuty v2 script: commands: - arguments: - - description: Show only the schedules whose name matches the query + - description: Show only the schedules whose name matches the query. name: query - - description: The limit for the amount of schedules to receive(Default is 25, max value is 100) + - description: The limit for the amount of schedules to receive(Default is 25, max value is 100). name: limit - description: Receive all schedules from PagerDuty + description: Receive all schedules from PagerDuty. name: PagerDuty-get-all-schedules outputs: - contextPath: PagerDuty.Schedules.id - description: The ID of the schedule + description: The ID of the schedule. type: string - contextPath: PagerDuty.Schedules.name - description: The name of the schedule + description: The name of the schedule. type: string - arguments: - default: true - description: (default and mandatory) The unique identifier of the schedule + description: (default and mandatory) The unique identifier of the schedule. name: scheduleID required: true - - description: The start of the date range Using ISO 8601 Representation. E.g. !PagerDutyGetUsersOnCall since=2011-05-06T17:00Z + - description: The start of the date range Using ISO 8601 Representation. Maximum range is 6 months and default is 1 month. E.g. !PagerDutyGetUsersOnCall since=2011-05-06T17:00Z. name: since - - description: The end of the date range + - description: The end of the date range. Maximum range is 6 months and default is 1 month. name: until - description: Returns the names and details of on call users at a certain time or by specific schedule + description: Returns the names and details of on call users at a certain time or by specific schedule. name: PagerDuty-get-users-on-call outputs: - contextPath: PagerDutyUser.id - description: User's ID + description: User's ID. type: string - contextPath: PagerDutyUser.Emails - description: Email of user + description: Email of user. type: string - contextPath: PagerDutyUser.Username - description: Username of person + description: Username of person. type: string - contextPath: PagerDutyUser.DisplayName - description: Display name of person + description: Display name of person. type: string - contextPath: PagerDutyUser.Role - description: Display role of person + description: Display role of person. type: string - contextPath: PagerDutyUser.TimeZone - description: The time zone of the user + description: The time zone of the user. type: string - arguments: - - description: The limit for the amount of users to receive(Default is 25, max value is 100) + - description: The limit for the amount of users to receive(Default is 25, max value is 100). name: limit - description: Filters the results, showing only on-call users for the specified escalation policy IDs. isArray: true @@ -100,90 +100,110 @@ script: - description: Filters the results, showing only on-call users for the specified schedule IDs. If the value is null, permanent on-call user are included due to direct user escalation policy targets. isArray: true name: schedule_ids - description: Returns the names and details of current on call personnel + description: Returns the names and details of current on call personnel. name: PagerDuty-get-users-on-call-now outputs: - contextPath: PagerDutyUser.ID - description: User's ID + description: User's ID. type: string - contextPath: PagerDutyUser.Email - description: Email of user + description: Email of user. type: string - contextPath: PagerDutyUser.Username - description: Username of person + description: Username of person. type: string - contextPath: PagerDutyUser.DisplayName - description: Display name of person + description: Display name of person. type: string - contextPath: PagerDutyUser.Role - description: Role of person + description: Role of person. type: string - contextPath: PagerDutyUser.TimeZone - description: The time zone of the user + description: The time zone of the user. type: string - arguments: - auto: PREDEFINED - description: Returns only the incidents currently in the passed status(es). Valid status options are triggered,acknowledged, and resolved. (Default values are triggered,acknowledged) + description: Returns only the incidents currently in the passed status(es). Valid status options are triggered,acknowledged, and resolved. (Default values are triggered,acknowledged). name: status predefined: - triggered - acknowledged - resolved - - description: Beginning date and time. Using ISO 8601 Representation. E.g. PagerDutyIncidents since=2011-05-06T17:00Z (must be used with until argument) + - description: Beginning date and time. Using ISO 8601 Representation. E.g. PagerDutyIncidents since=2011-05-06T17:00Z (must be used with until argument). name: since - - description: Used to specify both the field you wish to sort the results on, as well as the direction (ascending/descending) of the results.See more https://v2.developer.pagerduty.com/v2/page/api-reference#!/Incidents/get_incidents + - description: Used to specify both the field you wish to sort the results on, as well as the direction (ascending/descending) of the results.See more https://v2.developer.pagerduty.com/v2/page/api-reference#!/Incidents/get_incidents. name: sortBy - - description: Last date and time. Using ISO 8601 Representation. E.g. PagerDutyIncidents until=2016-05-06T13:00Z + - description: Last date and time. Using ISO 8601 Representation. E.g. PagerDutyIncidents until=2016-05-06T13:00Z. name: until - - description: Incident de-duplication key. E.g. 8e42eeb6391a4a2abeda5d12e09bddec + - description: Incident de-duplication key. E.g., 8e42eeb6391a4a2abeda5d12e09bddec. name: incident_key - description: Shows incidents in PagerDuty. Default status parameters are triggered,acknowledged + - description: The maximum number of incidents to retrieve. If "page_size" is defined, this argument is ignored. + name: limit + defaultValue: 50 + - description: "Comma separated list of User IDs. Returns only the incidents currently assigned to the passed user(s). Note: When using the assigned_to_user filter, you will only receive incidents with statuses of triggered or acknowledged. This is because resolved incidents are not assigned to any user." + name: user_id + - auto: PREDEFINED + description: Array of the urgencies of the incidents to be returned. Defaults to all urgencies. Account must have the urgencies ability to do this. + name: urgencies + predefined: + - high + - low + - auto: PREDEFINED + description: When set to all, the since and until parameters and defaults are ignored. + name: date_range + predefined: + - all + - description: The page number of incidents to retrieve (used for pagination) starting from 1. The page size is defined by the "page_size" argument. + name: page + - description: The number of incidents per page to retrieve (used for pagination). The page number is defined by the "page" argument. The maximum value is 100. + name: page_size + description: Shows incidents in PagerDuty. Default status parameters are triggered,acknowledged. name: PagerDuty-incidents outputs: - contextPath: PagerDuty.Incidents.ID - description: Incident ID + description: Incident ID. type: string - contextPath: PagerDuty.Incidents.Title - description: The title of the incident + description: The title of the incident. type: string - contextPath: PagerDuty.Incidents.Status - description: Incident Status + description: Incident Status. type: string - contextPath: PagerDuty.Incidents.created_at - description: Time in which the incident was created + description: Time in which the incident was created. type: date - contextPath: PagerDuty.Incidents.urgency - description: Incident Urgency + description: Incident Urgency. type: string - contextPath: PagerDuty.Incidents.assignee - description: 'The assignee of the incident ' + description: 'The assignee of the incident.' type: string - contextPath: PagerDuty.Incidents.service_id - description: The id of the impacted service + description: The id of the impacted service. type: string - contextPath: PagerDuty.Incidents.service_name - description: The name of the impacted service + description: The name of the impacted service. type: string - contextPath: PagerDuty.Incidents.escalation_policy - description: The escalation policy + description: The escalation policy. type: string - contextPath: PagerDuty.Incidents.last_status_change_at - description: Time in which the last status change occurred + description: Time in which the last status change occurred. type: date - contextPath: PagerDuty.Incidents.last_status_change_by - description: Name of the user who done the last status change + description: Name of the user who done the last status change. type: string - contextPath: PagerDuty.Incidents.number_of_escalations - description: Number of escalations that took place + description: Number of escalations that took place. type: number - contextPath: PagerDuty.Incidents.resolved_by - description: Name of the User who resolved the incident + description: Name of the User who resolved the incident. type: string - contextPath: PagerDuty.Incidents.resolve_reason - description: The reason for resolving the issue + description: The reason for resolving the issue. type: string - contextPath: PagerDuty.Incidents.Description - description: The Description of the incident + description: The Description of the incident. type: string - contextPath: PagerDuty.Incidents.teams.ID description: The ID of the team assigned for the incident. @@ -192,25 +212,25 @@ script: description: The name of the team assigned for the incident. type: string - contextPath: PagerDuty.Incidents.assignment.time - description: The time of the assignment to the incident + description: The time of the assignment to the incident. type: date - contextPath: PagerDuty.Incidents.assignment.assignee - description: The name of the assignee to the incident + description: The name of the assignee to the incident. type: string - contextPath: PagerDuty.Incidents.assignment.assigneeId - description: The ID of the assignee to the incident + description: The ID of the assignee to the incident. type: string - contextPath: PagerDuty.Incidents.acknowledgement.time - description: The time of the acknowledgement to the incident + description: The time of the acknowledgement to the incident. type: date - contextPath: PagerDuty.Incidents.acknowledgement.acknowledger - description: The name of the acknowledger to the incident + description: The name of the acknowledger to the incident. type: string - contextPath: PagerDuty.Incidents.acknowledgement.acknowledgerId - description: The ID of the acknowledger to the incident + description: The ID of the acknowledger to the incident. type: string - contextPath: PagerDuty.Incidents.incident_key - description: The incident's de-duplication key + description: The incident's de-duplication key. type: String - arguments: - description: Specific human-readable unique identifier, such as a hostname, for the system having the problem. @@ -220,7 +240,7 @@ script: name: summary required: true - auto: PREDEFINED - description: The severity of the event + description: The severity of the event. name: severity predefined: - critical @@ -229,150 +249,150 @@ script: - info required: true - auto: PREDEFINED - description: The action to be executed + description: The action to be executed. name: action predefined: - trigger - acknowledge - resolve required: true - - description: A short description of the problem + - description: A short description of the problem. name: description - - description: 'A cluster or grouping of sources. For example, sources “prod-datapipe-02” and “prod-datapipe-03” might both be part of “prod-datapipe”. Example: "prod-datapipe" "www"' + - description: 'A cluster or grouping of sources. For example, sources “prod-datapipe-02” and “prod-datapipe-03” might both be part of “prod-datapipe”. Example: "prod-datapipe" "www".' name: group - - description: 'The class/type of the event. Example: "High CPU" "Latency"' + - description: 'The class/type of the event. Example: "High CPU" "Latency".' name: event_class - - description: 'The part or component of the affected system that is broken. Example: "keepalive" "webping"' + - description: 'The part or component of the affected system that is broken. Example: "keepalive" "webping".' name: component - - description: Incident key, used to acknowledge/resolve specific event + - description: Incident key, used to acknowledge/resolve specific event. name: incident_key - - description: Service key for the integration + - description: Service key for the integration. name: serviceKey - description: Creates a new event/incident in PagerDuty(In order to use this command you have to enter the Service Key in the integration settings) + description: Creates a new event/incident in PagerDuty(In order to use this command you have to enter the Service Key in the integration settings). name: PagerDuty-submit-event outputs: - contextPath: PagerDuty.Event.Status - description: Status of the action on the event + description: Status of the action on the event. type: string - contextPath: PagerDuty.Event.incident_key - description: Incident key + description: Incident key. type: string - arguments: - - description: 'ID of the wanted user ' + - description: 'ID of the wanted user.' name: UserID required: true - description: Get the contact methods of a given user + description: Get the contact methods of a given user. name: PagerDuty-get-contact-methods outputs: - contextPath: PagerDuty.Contact_methods.phone - description: The phone number of the user + description: The phone number of the user. type: string - contextPath: PagerDuty.Contact_methods.id - description: ID of the contact method + description: ID of the contact method. type: string - contextPath: PagerDuty.Contact_methods.type - description: The type of the current contact method + description: The type of the current contact method. type: string - contextPath: PagerDuty.Contact_methods.email - description: The email of the user + description: The email of the user. type: string - arguments: - - description: ID of the wanted user + - description: ID of the wanted user. name: UserID required: true - description: Get the users notification rules + description: Get the users notification rules. name: PagerDuty-get-users-notification outputs: - contextPath: PagerDuty.Notification_rules.start_delay_in_minutes - description: The delay time for notifying the user + description: The delay time for notifying the user. type: string - contextPath: PagerDuty.Notification_rules.urgency - description: The urgency of the notification + description: The urgency of the notification. type: string - contextPath: PagerDuty.Notification_rules.id - description: The id of the notification rule + description: The id of the notification rule. type: string - arguments: - - description: Incident key + - description: Incident key. name: incident_key required: true - - description: Service key for the integration + - description: Service key for the integration. name: serviceKey - description: Resolves an existing event in PagerDuty + description: Resolves an existing event in PagerDuty. name: PagerDuty-resolve-event outputs: - contextPath: PagerDuty.Event.Status - description: Status of the action on the event + description: Status of the action on the event. type: string - contextPath: PagerDuty.Event.incident_key - description: Incident key + description: Incident key. type: string - arguments: - - description: Incident key + - description: Incident key. name: incident_key required: true - - description: Service key for the integration + - description: Service key for the integration. name: serviceKey - description: Acknowledges an existing event in PagerDuty + description: Acknowledges an existing event in PagerDuty. name: PagerDuty-acknowledge-event outputs: - contextPath: PagerDuty.Event.Status - description: Status of the action on the event + description: Status of the action on the event. type: string - contextPath: PagerDuty.Event.incident_key - description: Incident key + description: Incident key. type: string - arguments: - description: ID of the incident to get information for. name: incident_id required: true - description: Get data about a incident from PagerDuty + description: Get data about a incident from PagerDuty. name: PagerDuty-get-incident-data outputs: - contextPath: PagerDuty.Incidents.ID - description: Incident ID + description: Incident ID. type: string - contextPath: PagerDuty.Incidents.Title - description: The title of the incident + description: The title of the incident. type: string - contextPath: PagerDuty.Incidents.Status - description: Incident Status + description: Incident Status. type: string - contextPath: PagerDuty.Incidents.created_at - description: Time in which the incident was created + description: Time in which the incident was created. type: date - contextPath: PagerDuty.Incidents.urgency - description: Incident Urgency + description: Incident Urgency. type: string - contextPath: PagerDuty.Incidents.assignee - description: The assignee of the incident + description: The assignee of the incident. type: string - contextPath: PagerDuty.Incidents.service_id - description: The id of the impacted service + description: The id of the impacted service. type: string - contextPath: PagerDuty.Incidents.service_name - description: The name of the impacted service + description: The name of the impacted service. type: string - contextPath: PagerDuty.Incidents.escalation_policy - description: The escalation policy + description: The escalation policy. type: string - contextPath: PagerDuty.Incidents.last_status_change_at - description: Time in which the last status change occurred + description: Time in which the last status change occurred. type: date - contextPath: PagerDuty.Incidents.last_status_change_by - description: Name of the user who done the last status change + description: Name of the user who done the last status change. type: string - contextPath: PagerDuty.Incidents.number_of_escalations - description: Number of escalations that took place + description: Number of escalations that took place. type: number - contextPath: PagerDuty.Incidents.resolved_by - description: Name of the User who resolved the incident + description: Name of the User who resolved the incident. type: string - contextPath: PagerDuty.Incidents.resolve_reason - description: The reason for resolving the issue + description: The reason for resolving the issue. type: string - contextPath: PagerDuty.Incidents.Description - description: The Description of the incident + description: The Description of the incident. type: string - contextPath: PagerDuty.Incidents.teams.ID description: The ID of the team assigned for the incident. @@ -381,90 +401,90 @@ script: description: The name of the team assigned for the incident. type: string - contextPath: PagerDuty.Incidents.assignment.time - description: The time of the assignment to the incident + description: The time of the assignment to the incident. type: date - contextPath: PagerDuty.Incidents.assignment.assignee - description: The name of the assignee to the incident + description: The name of the assignee to the incident. type: string - contextPath: PagerDuty.Incidents.assignment.assigneeId - description: The ID of the assignee to the incident + description: The ID of the assignee to the incident. type: string - contextPath: PagerDuty.Incidents.acknowledgement.time - description: The time of the acknowledgement to the incident + description: The time of the acknowledgement to the incident. type: date - contextPath: PagerDuty.Incidents.acknowledgement.acknowledger - description: The name of the acknowledger to the incident + description: The name of the acknowledger to the incident. type: string - contextPath: PagerDuty.Incidents.acknowledgement.acknowledgerId - description: The ID of the acknowledger to the incident + description: The ID of the acknowledger to the incident. type: string - contextPath: PagerDuty.Incidents.incident_key - description: The incident's de-duplication key + description: The incident's de-duplication key. type: String - - description: Get Service keys for each of the services configured in the PagerDuty instance + - description: Get Service keys for each of the services configured in the PagerDuty instance. name: PagerDuty-get-service-keys outputs: - contextPath: PagerDuty.Service.ID - description: The ID of the service connected to PagerDuty + description: The ID of the service connected to PagerDuty. type: string - contextPath: PagerDuty.Service.Name - description: The name of the service connected to PagerDuty + description: The name of the service connected to PagerDuty. type: string - contextPath: PagerDuty.Service.Status - description: The status of the service connected to PagerDuty + description: The status of the service connected to PagerDuty. type: string - contextPath: PagerDuty.Service.CreatedAt - description: The date in which the service connected to PagerDuty was created + description: The date in which the service connected to PagerDuty was created. type: date - contextPath: PagerDuty.Service.Integration.Name - description: The name of the integration used with the service + description: The name of the integration used with the service. type: string - contextPath: PagerDuty.Service.Integration.Vendor - description: The name of the vendor for the integration used with the service.(A value of 'Missing Vendor information' will appear once no information could be found) + description: The name of the vendor for the integration used with the service.(A value of 'Missing Vendor information' will appear once no information could be found). type: string - contextPath: PagerDuty.Service.Integration.Key - description: The key used to control events with the integration + description: The key used to control events with the integration. type: string - arguments: - - description: PagerDuty Incident ID to add responders to + - description: PagerDuty Incident ID to add responders to. name: incident_id required: true - - description: Message to send to responders + - description: Message to send to responders. name: message required: true - - description: Comma separated list of User IDs to request response from + - description: Comma separated list of User IDs to request response from. name: user_requests - - description: Comma separated list of Escalation Policy IDs to request response from + - description: Comma separated list of Escalation Policy IDs to request response from. name: escalation_policy_requests - - description: UserID sending the request (if blank, uses the default for the integration) + - description: UserID sending the request (if blank, uses the default for the integration). name: requestor_id - description: Add responders to an incident + description: Add responders to an incident. name: PagerDuty-add-responders outputs: - contextPath: PagerDuty.ResponderRequests.ResponderID - description: The User ID of the responder added + description: The user ID of the responder added. type: String - contextPath: PagerDuty.ResponderRequests.ResponderName - description: The name of the responder added + description: The name of the responder added. type: String - arguments: - - description: The PagerDuty Incident ID to run the play on + - description: The PagerDuty incident ID to run the play on. name: incident_id required: true - - description: User's email to trigger the response play from + - description: User's email to trigger the response play from. name: from_email required: true - - description: The UUID of the response play to run + - description: The UUID of the response play to run. name: response_play_uuid required: true name: PagerDuty-run-response-play - description: Run a response play on PagerDuty (based on its UUID) + description: Run a response play on PagerDuty (based on its UUID). isfetch: true runonce: false script: '-' subtype: python3 type: python - dockerimage: demisto/python3:3.10.12.63474 + dockerimage: demisto/python3:3.10.13.81631 tests: - PagerDuty Test fromversion: 5.0.0 diff --git a/Packs/PagerDuty/Integrations/PagerDuty/PagerDuty_test.py b/Packs/PagerDuty/Integrations/PagerDuty/PagerDuty_test.py index 10eb792e441c..06e94ee6d637 100644 --- a/Packs/PagerDuty/Integrations/PagerDuty/PagerDuty_test.py +++ b/Packs/PagerDuty/Integrations/PagerDuty/PagerDuty_test.py @@ -1,9 +1,10 @@ -# -*- coding: utf-8 -*- from CommonServerPython import * import pytest +from pytest_mock import MockerFixture +from requests_mock import MockerCore -def load_mock_response(file_name): +def load_mock_response(file_name: str) -> dict: """ Load mock file that simulates an API response. @@ -14,11 +15,11 @@ def load_mock_response(file_name): str: Mock file content. """ - with open('test_data/' + file_name, mode='r') as f: + with open(f'test_data/{file_name}') as f: return json.loads(f.read()) -def test_get_incidents(requests_mock, mocker): +def test_get_incidents(requests_mock: MockerCore, mocker: MockerFixture) -> None: """ Given: - An incident with non-ascii character in its documentation @@ -56,11 +57,11 @@ def test_get_incidents(requests_mock, mocker): }] } ) - res = get_incidents_command() + res = get_incidents_command({}) assert '| Documentation: • |' in res['HumanReadable'] -def test_add_responders(requests_mock, mocker): +def test_add_responders(requests_mock: MockerCore, mocker: MockerFixture) -> None: """ Given: - a responder request. @@ -92,19 +93,19 @@ def test_add_responders(requests_mock, mocker): ) requests_mock.post( 'https://api.pagerduty.com/incidents/PXP12GZ/responder_requests', - json=load_mock_response('responder_requests.json').get('specific_users') + json=load_mock_response('responder_requests.json')['specific_users'] ) from PagerDuty import add_responders_to_incident res = add_responders_to_incident(**demisto.args()) - expected_users_requested = ','.join([x.get("ID") for x in res.outputs]) - assert demisto.args().get('incident_id') == res.outputs[0].get('IncidentID') - assert demisto.args().get('message') == res.outputs[0].get('Message') - assert demisto.params().get('DefaultRequestor') == res.outputs[1].get('RequesterID') - assert demisto.args().get('user_requests') == expected_users_requested + expected_users_requested = ','.join([x["ID"] for x in res.outputs]) + assert demisto.args()['incident_id'] == res.outputs[0]['IncidentID'] + assert demisto.args()['message'] == res.outputs[0]['Message'] + assert demisto.params()['DefaultRequestor'] == res.outputs[1]['RequesterID'] + assert demisto.args()['user_requests'] == expected_users_requested -def test_add_responders_default(requests_mock, mocker): +def test_add_responders_default(requests_mock: MockerCore, mocker: MockerFixture) -> None: """ Given: - a responder request without specifying responders. @@ -135,19 +136,19 @@ def test_add_responders_default(requests_mock, mocker): ) requests_mock.post( 'https://api.pagerduty.com/incidents/PXP12GZ/responder_requests', - json=load_mock_response('responder_requests.json').get('default_user') + json=load_mock_response('responder_requests.json')['default_user'] ) from PagerDuty import add_responders_to_incident res = add_responders_to_incident(**demisto.args()) - expected_users_requested = ','.join([x.get("ID") for x in res.outputs]) - assert demisto.args().get('incident_id') == res.outputs[0].get('IncidentID') - assert demisto.args().get('message') == res.outputs[0].get('Message') - assert demisto.params().get('DefaultRequestor') == res.outputs[0].get('RequesterID') - assert demisto.params().get('DefaultRequestor') == expected_users_requested + expected_users_requested = ','.join([x["ID"] for x in res.outputs]) + assert demisto.args()['incident_id'] == res.outputs[0]['IncidentID'] + assert demisto.args()['message'] == res.outputs[0]['Message'] + assert demisto.params()['DefaultRequestor'] == res.outputs[0]['RequesterID'] + assert demisto.params()['DefaultRequestor'] == expected_users_requested -def test_play_response_play(requests_mock, mocker): +def test_play_response_play(requests_mock: MockerCore, mocker: MockerFixture) -> None: """ Given: - a responder request without specifying responders. @@ -188,7 +189,7 @@ def test_play_response_play(requests_mock, mocker): assert res.raw_response == {"status": "ok"} -def test_get_users_on_call(requests_mock, mocker): +def test_get_users_on_call(requests_mock: MockerCore, mocker: MockerFixture) -> None: """ Given: - a request to get user on-call by schedule ID. @@ -222,10 +223,10 @@ def test_get_users_on_call(requests_mock, mocker): ) from PagerDuty import get_on_call_users_command res = get_on_call_users_command(**demisto.args()) - assert demisto.args().get('scheduleID') == res.outputs[0].get('ScheduleID') + assert demisto.args()['scheduleID'] == res.outputs[0]['ScheduleID'] -def test_get_users_on_call_now(requests_mock, mocker): +def test_get_users_on_call_now(requests_mock: MockerCore, mocker: MockerFixture) -> None: """ Given: - a reqest to get user oncall by schedule ID without specifying responders. @@ -259,11 +260,11 @@ def test_get_users_on_call_now(requests_mock, mocker): ) from PagerDuty import get_on_call_now_users_command res = get_on_call_now_users_command(**demisto.args()) - assert res.outputs[0].get('ScheduleID') in demisto.args().get('schedule_ids') + assert res.outputs[0]['ScheduleID'] in demisto.args()['schedule_ids'] assert 'oncalls' in res.raw_response -def test_submit_event(requests_mock, mocker): +def test_submit_event(requests_mock: MockerCore, mocker: MockerFixture) -> None: """ Given: - a reqest to submit request. @@ -299,10 +300,10 @@ def test_submit_event(requests_mock, mocker): ) from PagerDuty import submit_event_command res = submit_event_command(source, summary, severity, action) - assert '### Trigger Event' in res.get('HumanReadable') + assert '### Trigger Event' in res['HumanReadable'] -def test_get_all_schedules_command(mocker, requests_mock): +def test_get_all_schedules_command(mocker: MockerFixture, requests_mock: MockerCore) -> None: """ Given: - a reqest to get all schedule @@ -336,10 +337,10 @@ def test_get_all_schedules_command(mocker, requests_mock): ) from PagerDuty import get_all_schedules_command res = get_all_schedules_command() - assert '### All Schedules' in res.get('HumanReadable') + assert '### All Schedules' in res['HumanReadable'] -def test_get_users_contact_methods_command(mocker, requests_mock): +def test_get_users_contact_methods_command(mocker: MockerFixture, requests_mock: MockerCore) -> None: """ Given: - a reqest to get all schedule. @@ -369,10 +370,10 @@ def test_get_users_contact_methods_command(mocker, requests_mock): ) from PagerDuty import get_users_contact_methods_command res = get_users_contact_methods_command(user_id) - assert '### Contact Methods' in res.get('HumanReadable') + assert '### Contact Methods' in res['HumanReadable'] -def test_get_users_notification_command(mocker, requests_mock): +def test_get_users_notification_command(mocker: MockerFixture, requests_mock: MockerCore) -> None: """ Given: - a request to get users notifications. @@ -402,11 +403,11 @@ def test_get_users_notification_command(mocker, requests_mock): ) from PagerDuty import get_users_notification_command res = get_users_notification_command(user_id) - assert '### User notification rules' in res.get('HumanReadable') + assert '### User notification rules' in res['HumanReadable'] @pytest.mark.parametrize('severity, expected_result', [('high', 3), ('low', 1), ('other_severity', 0)]) -def test_translate_severity(mocker, severity, expected_result): +def test_translate_severity(mocker: MockerFixture, severity: str, expected_result: int) -> None: """ Given: - a severity. @@ -428,3 +429,98 @@ def test_translate_severity(mocker, severity, expected_result): from PagerDuty import translate_severity res = translate_severity(severity) assert res == expected_result + + +def test_paginate_with_limit(mocker: MockerFixture): + """This test verifies that the function correctly handles pagination when a limit is provided, + making a single API request with the expected parameters and returning the correct results. + Given: + a test scenario where the `pagination_incidents` function is called with a specified limit, + When: + the function is invoked with a limit of 79, + Then: + it should make a single API request with the specified limit and offset 0, + and the result should match the mocked API response. + """ + from PagerDuty import pagination_incidents + + re = mocker.patch( + "PagerDuty.http_request", + side_effect=[ + {"incidents": list(range(79))} + ], + ) + + result = pagination_incidents({"user_ids": "test_id"}, {"limit": 79}, "") + + assert result == list(range(79)) + assert re.call_count == 1 + assert re.call_args_list[0].args == ("GET", "", {"user_ids": "test_id", "limit": 79, "offset": 0}) + + +def test_paginate_with_limit_is_more_than_INCIDENT_API_LIMIT(mocker: MockerFixture): + """This test ensures that the function correctly handles pagination for large limits, + making multiple API calls to retrieve all incidents. + + Given: + a test scenario where the requested limit exceeds the max incidents per page (100), + When: + the `pagination_incidents` function is called with a limit of 179, + Then: + it should make two API calls: + - First call with limit 100 and offset 0. + - Second call with limit 79 (to fetch the remaining incidents) and offset 100. + + """ + from PagerDuty import pagination_incidents + + re = mocker.patch( + "PagerDuty.http_request", + side_effect=[ + {"incidents": list(range(100))}, # the response for the first call + {"incidents": list(range(100, 179))}, # the response for the secund call + ], + ) + + result = pagination_incidents({"user_ids": "test_id"}, {"limit": 179}, "") + + assert result == list(range(179)) + assert re.call_count == 2 + assert re.call_args_list[0].args == ("GET", "", {"user_ids": "test_id", "limit": 100, "offset": 0}) # first call + assert re.call_args_list[1].args == ("GET", "", {"user_ids": "test_id", "limit": 79, "offset": 100}) # secund call + + +def test_paginate_with_page_size(mocker: MockerFixture): + """This test verifies that the pagination functionality correctly handles the provided page size + and page number, making a single API request with the expected parameters. + Given: + a test scenario where pagination is performed with a specified page size, + When: + the `pagination_incidents` function is called with a page size of 100 and page number 2, + Then: + it should make a single API request to fetch results from offset 100 to 199. + """ + from PagerDuty import pagination_incidents + + re = mocker.patch( + "PagerDuty.http_request", side_effect=[{"incidents": list(range(100, 200))}] + ) + result = pagination_incidents({"user_ids": "test_id"}, {"page_size": 100, "page": 2}, "") + assert result == list(range(100, 200)) + assert re.call_count == 1 + assert re.call_args_list[0].args == ('GET', '', {'user_ids': 'test_id', 'limit': 100, 'offset': 100}) + + +def test_paginate_with_page_size_more_than_INCIDENT_API_LIMIT(): + """This test ensures that the function correctly handles the case where the provided page size exceeds the API limit, + raising a DemistoException with the appropriate error message. + Given: + a test scenario where the `pagination_incidents` function is called with a page size greater than the API limit, + When: + the function is invoked with a page size of 200 and page number 2, + Then: + it should raise a DemistoException with the message "The max size for page is 100. Please provide a smaller page size." + """ + from PagerDuty import pagination_incidents + with pytest.raises(DemistoException, match="The max size for page is 100. Please provide a lower page size."): + pagination_incidents({"user_ids": "test_id"}, {"page_size": 200, "page": 2}, "") diff --git a/Packs/PagerDuty/Integrations/PagerDuty/README.md b/Packs/PagerDuty/Integrations/PagerDuty/README.md index 01b8ce8d2d4c..20be3ac321c4 100644 --- a/Packs/PagerDuty/Integrations/PagerDuty/README.md +++ b/Packs/PagerDuty/Integrations/PagerDuty/README.md @@ -1,28 +1,31 @@ Use the PagerDuty integration to manage schedules and on-call users. This integration was integrated and tested with PagerDuty API v2. + ## Configure PagerDuty v2 on Cortex XSOAR 1. Navigate to **Settings** > **Integrations** > **Servers & Services**. 2. Search for PagerDuty v2. 3. Click **Add instance** to create and configure a new integration instance. - | **Parameter** | **Required** | - |--------------| --- | - | API Key | True | - | Service Key (for triggering events only) | False | - | Trust any certificate (not secure) | False | - | Use system proxy settings | False | - | Fetch incidents | False | - | Incident type | False | - | Default Requestor | False | - | Initial Fetch Interval (In minutes, used only for the first fetch or after Reset last run) | False | + | **Parameter** | **Required** | + | --- | --- | + | API Key | False | + | Service Key (for triggering, acknowledging and resolving events only) | False | + | Trust any certificate (not secure) | False | + | Use system proxy settings | False | + | Fetch incidents | False | + | Incident type | False | + | Initial Fetch Interval (In minutes, used only for first fetch or after Reset last run) | False | + | Default requestor ID for adding people to incidents | False | 4. Click **Test** to validate the URLs, token, and connection. ## Fetched Incidents Data + By default, the integration will import PagerDuty incidents data as Cortex XSOAR incidents. All incidents created in the minute prior to the configuration of Fetch Incidents and up to current time will be imported. ## Commands + You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook. After you successfully execute a command, a DBot message appears in the War Room with the command details. @@ -41,13 +44,14 @@ After you successfully execute a command, a DBot message appears in the War Room 13. [Run response play to an incident: PagerDuty-run-response-play](#pagerduty-run-response-play) ### PagerDuty-get-all-schedules -*** -Receive all schedules from PagerDuty +*** +Receive all schedules from PagerDuty. #### Base Command `PagerDuty-get-all-schedules` + #### Input | **Argument Name** | **Description** | **Required** | @@ -55,19 +59,20 @@ Receive all schedules from PagerDuty | query | Show only the schedules whose name matches the query. | Optional | | limit | The limit for the amount of schedules to receive(Default is 25, max value is 100). | Optional | - #### Context Output | **Path** | **Type** | **Description** | | --- | --- | --- | -| PagerDuty.Schedules.id | string | The ID of the schedule | -| PagerDuty.Schedules.name | string | The name of the schedule | +| PagerDuty.Schedules.id | string | The ID of the schedule. | +| PagerDuty.Schedules.name | string | The name of the schedule. | #### Command Example + ```!PagerDuty-get-all-schedules``` #### Context Example + ```json { "PagerDuty": { @@ -104,6 +109,7 @@ Receive all schedules from PagerDuty #### Human Readable Output >### All Schedules + >|ID|Name|Today|Time Zone|Escalation Policy|Escalation Policy ID| >|---|---|---|---|---|---| >| scheduleid | New Schedule #1 | 2021-03-10 | America/Los_Angeles | Default | someid | @@ -111,38 +117,39 @@ Receive all schedules from PagerDuty ### PagerDuty-get-users-on-call -*** -Returns the names and details of on call users at a certain time or by specific schedule +*** +Returns the names and details of on call users at a certain time or by specific schedule. #### Base Command `PagerDuty-get-users-on-call` + #### Input | **Argument Name** | **Description** | **Required** | | --- | --- | --- | | scheduleID | (default and mandatory) The unique identifier of the schedule. | Required | -| since | The start of the date range Using ISO 8601 Representation. E.g. !PagerDutyGetUsersOnCall since=2011-05-06T17:00Z. | Optional | -| until | The end of the date range. | Optional | - +| since | The start of the date range Using ISO 8601 Representation. Maximum range is 6 months and default is 1 month. E.g. !PagerDutyGetUsersOnCall since=2011-05-06T17:00Z. | Optional | +| until | The end of the date range. Maximum range is 6 months and default is 1 month. | Optional | #### Context Output | **Path** | **Type** | **Description** | | --- | --- | --- | -| PagerDutyUser.id | string | User's ID | -| PagerDutyUser.Emails | string | Email of user | -| PagerDutyUser.Username | string | Username of person | -| PagerDutyUser.DisplayName | string | Display name of person | -| PagerDutyUser.Role | string | Display role of person | -| PagerDutyUser.TimeZone | string | The time zone of the user | - +| PagerDutyUser.id | string | User's ID. | +| PagerDutyUser.Emails | string | Email of user. | +| PagerDutyUser.Username | string | Username of person. | +| PagerDutyUser.DisplayName | string | Display name of person. | +| PagerDutyUser.Role | string | Display role of person. | +| PagerDutyUser.TimeZone | string | The time zone of the user. | #### Command Example + ```!PagerDuty-get-users-on-call scheduleID=scheduleid``` #### Context Example + ```json { "PagerDutyUser": [ @@ -169,20 +176,22 @@ Returns the names and details of on call users at a certain time or by specific #### Human Readable Output >### Users On Call + >|ID|Email|Name|Role|User Url|Time Zone| >|---|---|---|---|---|---| ->| someid | demisto@demisto.com | Demisto User | owner | https://demisto.pagerduty.com/users/someid | Europe/Athens | ->| anotherid | demisto@mail.com | Another User | user | https://demisto.pagerduty.com/users/anotherid | Europe/Athens | +>| someid | demisto@demisto.com | Demisto User | owner | | Europe/Athens | +>| anotherid | demisto@mail.com | Another User | user | | Europe/Athens | ### PagerDuty-get-users-on-call-now -*** -Returns the names and details of current on call personnel +*** +Returns the names and details of current on call personnel. #### Base Command `PagerDuty-get-users-on-call-now` + #### Input | **Argument Name** | **Description** | **Required** | @@ -191,23 +200,24 @@ Returns the names and details of current on call personnel | escalation_policy_ids | Filters the results, showing only on-call users for the specified escalation policy IDs. | Optional | | schedule_ids | Filters the results, showing only on-call users for the specified schedule IDs. If the value is null, permanent on-call user are included due to direct user escalation policy targets. | Optional | - #### Context Output | **Path** | **Type** | **Description** | | --- | --- | --- | -| PagerDutyUser.ID | string | User's ID | -| PagerDutyUser.Email | string | Email of user | -| PagerDutyUser.Username | string | Username of person | -| PagerDutyUser.DisplayName | string | Display name of person | -| PagerDutyUser.Role | string | Role of person | -| PagerDutyUser.TimeZone | string | The time zone of the user | +| PagerDutyUser.ID | string | User's ID. | +| PagerDutyUser.Email | string | Email of user. | +| PagerDutyUser.Username | string | Username of person. | +| PagerDutyUser.DisplayName | string | Display name of person. | +| PagerDutyUser.Role | string | Role of person. | +| PagerDutyUser.TimeZone | string | The time zone of the user. | #### Command Example + ```!PagerDuty-get-users-on-call-now``` #### Context Example + ```json { "PagerDutyUser": [ @@ -226,63 +236,71 @@ Returns the names and details of current on call personnel #### Human Readable Output >### Users On Call Now + >|ID|Email|Name|Role|User Url|Time Zone| >|---|---|---|---|---|---| ->| someid | demisto@demisto.com | Demisto User | owner | https://demisto.pagerduty.com/users/someid | Europe/Athens | +>| someid | demisto@demisto.com | Demisto User | owner | | Europe/Athens | ### PagerDuty-incidents -*** -Shows incidents in PagerDuty. Default status parameters are triggered,acknowledged +*** +Shows incidents in PagerDuty. Default status parameters are triggered,acknowledged. #### Base Command `PagerDuty-incidents` + #### Input | **Argument Name** | **Description** | **Required** | | --- | --- | --- | | status | Returns only the incidents currently in the passed status(es). Valid status options are triggered,acknowledged, and resolved. (Default values are triggered,acknowledged). Possible values are: triggered, acknowledged, resolved. | Optional | | since | Beginning date and time. Using ISO 8601 Representation. E.g. PagerDutyIncidents since=2011-05-06T17:00Z (must be used with until argument). | Optional | -| sortBy | Used to specify both the field you wish to sort the results on, as well as the direction (ascending/descending) of the results.See more https://v2.developer.pagerduty.com/v2/page/api-reference#!/Incidents/get_incidents. | Optional | +| sortBy | Used to specify both the field you wish to sort the results on, as well as the direction (ascending/descending) of the results.See more . | Optional | | until | Last date and time. Using ISO 8601 Representation. E.g. PagerDutyIncidents until=2016-05-06T13:00Z. | Optional | -| incident_key | Incident de-duplication key,. | Optional | - +| incident_key | Incident de-duplication key. E.g., 8e42eeb6391a4a2abeda5d12e09bddec. | Optional | +| limit | The maximum number of incidents to retrieve. If "page_size" is defined, this argument is ignored. Default is 50. | Optional | +| user_id | Comma separated list of User IDs. Returns only the incidents currently assigned to the passed user(s). Note: When using the assigned_to_user filter, you will only receive incidents with statuses of triggered or acknowledged. This is because resolved incidents are not assigned to any user. | Optional | +| urgencies | Array of the urgencies of the incidents to be returned. Defaults to all urgencies. Account must have the urgencies ability to do this. Possible values are: high, low. | Optional | +| date_range | When set to all, the since and until parameters and defaults are ignored. Possible values are: all. | Optional | +| page | The page number of incidents to retrieve (used for pagination) starting from 1. The page size is defined by the "page_size" argument. | Optional | +| page_size | The number of incidents per page to retrieve (used for pagination). The page number is defined by the "page" argument. The maximum value is 100. | Optional | #### Context Output | **Path** | **Type** | **Description** | | --- | --- | --- | -| PagerDuty.Incidents.ID | string | Incident ID | -| PagerDuty.Incidents.Title | string | The title of the incident | -| PagerDuty.Incidents.Status | string | Incident Status | -| PagerDuty.Incidents.created_at | date | Time in which the incident was created | -| PagerDuty.Incidents.urgency | string | Incident Urgency | -| PagerDuty.Incidents.assignee | string | The assignee of the incident | -| PagerDuty.Incidents.service_id | string | The id of the impacted service | -| PagerDuty.Incidents.service_name | string | The name of the impacted service | -| PagerDuty.Incidents.escalation_policy | string | The escalation policy | -| PagerDuty.Incidents.last_status_change_at | date | Time in which the last status change occurred | -| PagerDuty.Incidents.last_status_change_by | string | Name of the user who done the last status change | -| PagerDuty.Incidents.number_of_escalations | number | Number of escalations that took place | -| PagerDuty.Incidents.resolved_by | string | Name of the User who resolved the incident | -| PagerDuty.Incidents.resolve_reason | string | The reason for resolving the issue | -| PagerDuty.Incidents.Description | string | The Description of the incident | +| PagerDuty.Incidents.ID | string | Incident ID. | +| PagerDuty.Incidents.Title | string | The title of the incident. | +| PagerDuty.Incidents.Status | string | Incident Status. | +| PagerDuty.Incidents.created_at | date | Time in which the incident was created. | +| PagerDuty.Incidents.urgency | string | Incident Urgency. | +| PagerDuty.Incidents.assignee | string | The assignee of the incident. | +| PagerDuty.Incidents.service_id | string | The id of the impacted service. | +| PagerDuty.Incidents.service_name | string | The name of the impacted service. | +| PagerDuty.Incidents.escalation_policy | string | The escalation policy. | +| PagerDuty.Incidents.last_status_change_at | date | Time in which the last status change occurred. | +| PagerDuty.Incidents.last_status_change_by | string | Name of the user who done the last status change. | +| PagerDuty.Incidents.number_of_escalations | number | Number of escalations that took place. | +| PagerDuty.Incidents.resolved_by | string | Name of the User who resolved the incident. | +| PagerDuty.Incidents.resolve_reason | string | The reason for resolving the issue. | +| PagerDuty.Incidents.Description | string | The Description of the incident. | | PagerDuty.Incidents.teams.ID | string | The ID of the team assigned for the incident. | | PagerDuty.Incidents.teams.ID | string | The name of the team assigned for the incident. | -| PagerDuty.Incidents.assignment.time | date | The time of the assignment to the incident | -| PagerDuty.Incidents.assignment.assignee | string | The name of the assignee to the incident | -| PagerDuty.Incidents.assignment.assigneeId | string | The ID of the assignee to the incident | -| PagerDuty.Incidents.acknowledgement.time | date | The time of the acknowledgement to the incident | -| PagerDuty.Incidents.acknowledgement.acknowledger | string | The name of the acknowledger to the incident | -| PagerDuty.Incidents.acknowledgement.acknowledgerId | string | The ID of the acknowledger to the incident | -| PagerDuty.Incidents.incident_key | String | The incident's de-duplication key | - +| PagerDuty.Incidents.assignment.time | date | The time of the assignment to the incident. | +| PagerDuty.Incidents.assignment.assignee | string | The name of the assignee to the incident. | +| PagerDuty.Incidents.assignment.assigneeId | string | The ID of the assignee to the incident. | +| PagerDuty.Incidents.acknowledgement.time | date | The time of the acknowledgement to the incident. | +| PagerDuty.Incidents.acknowledgement.acknowledger | string | The name of the acknowledger to the incident. | +| PagerDuty.Incidents.acknowledgement.acknowledgerId | string | The ID of the acknowledger to the incident. | +| PagerDuty.Incidents.incident_key | String | The incident's de-duplication key. | #### Command Example + ```!PagerDuty-incidents``` #### Context Example + ```json { "PagerDuty": { @@ -353,19 +371,21 @@ Shows incidents in PagerDuty. Default status parameters are triggered,acknowledg #### Human Readable Output >### PagerDuty Incidents + >|ID|Title|Description|Status|Created On|Urgency|Html Url|Incident key|Assigned To User|Service ID|Service Name|Escalation Policy|Last Status Change On|Last Status Change By|Resolved By User| >|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| ->| someid | [#264] Ticket 01439490 | description: No description | acknowledged | 2021-03-04T08:52:56Z | high | https://demisto.pagerduty.com/incidents/someid | | someone | P5CX6RZ | PD SF | Default | 2021-03-04T08:53:04Z | someone | - | ->| anotherid | [#278] my event | description: No description | triggered | 2021-03-10T07:57:16Z | high | https://demisto.pagerduty.com/incidents/anotherid | somekey | someone-else | someid | API Service | Default | 2021-03-10T08:37:17Z | API Service | - | +>| someid | [#264] Ticket 01439490 | description: No description | acknowledged | 2021-03-04T08:52:56Z | high | | | someone | P5CX6RZ | PD SF | Default | 2021-03-04T08:53:04Z | someone | - | +>| anotherid | [#278] my event | description: No description | triggered | 2021-03-10T07:57:16Z | high | | somekey | someone-else | someid | API Service | Default | 2021-03-10T08:37:17Z | API Service | - | ### PagerDuty-submit-event -*** -Creates a new event/incident in PagerDuty(In order to use this command you have to enter the Service Key in the integration settings) +*** +Creates a new event/incident in PagerDuty(In order to use this command you have to enter the Service Key in the integration settings). #### Base Command `PagerDuty-submit-event` + #### Input | **Argument Name** | **Description** | **Required** | @@ -381,53 +401,55 @@ Creates a new event/incident in PagerDuty(In order to use this command you have | incident_key | Incident key, used to acknowledge/resolve specific event. | Optional | | serviceKey | Service key for the integration. | Optional | - #### Context Output | **Path** | **Type** | **Description** | | --- | --- | --- | -| PagerDuty.Event.Status | string | Status of the action on the event | -| PagerDuty.Event.incident_key | string | Incident key | +| PagerDuty.Event.Status | string | Status of the action on the event. | +| PagerDuty.Event.incident_key | string | Incident key. | #### Command Example + ```!PagerDuty-submit-event action=trigger severity=info source=demisto summary="my new event"``` #### Human Readable Output + >|Incident key|Message|Status| >|---|---|---| >| somekey | Event processed | success | ### PagerDuty-get-contact-methods -*** -Get the contact methods of a given user +*** +Get the contact methods of a given user. #### Base Command `PagerDuty-get-contact-methods` + #### Input | **Argument Name** | **Description** | **Required** | | --- | --- | --- | -| UserID | ID of the wanted user . | Required | - +| UserID | ID of the wanted user. | Required | #### Context Output | **Path** | **Type** | **Description** | | --- | --- | --- | -| PagerDuty.Contact_methods.phone | string | The phone number of the user | -| PagerDuty.Contact_methods.id | string | ID of the contact method | -| PagerDuty.Contact_methods.type | string | The type of the current contact method | -| PagerDuty.Contact_methods.email | string | The email of the user | - +| PagerDuty.Contact_methods.phone | string | The phone number of the user. | +| PagerDuty.Contact_methods.id | string | ID of the contact method. | +| PagerDuty.Contact_methods.type | string | The type of the current contact method. | +| PagerDuty.Contact_methods.email | string | The email of the user. | #### Command Example + ```!PagerDuty-get-contact-methods UserID=someid``` #### Context Example + ```json { "PagerDuty": { @@ -472,6 +494,7 @@ Get the contact methods of a given user #### Human Readable Output >### Contact Methods + >|ID|Type|Details| >|---|---|---| >| someotherid | Email | demisto@demisto.com | @@ -480,33 +503,35 @@ Get the contact methods of a given user ### PagerDuty-get-users-notification -*** -Get the users notification rules +*** +Get the users notification rules. #### Base Command `PagerDuty-get-users-notification` + #### Input | **Argument Name** | **Description** | **Required** | | --- | --- | --- | | UserID | ID of the wanted user. | Required | - #### Context Output | **Path** | **Type** | **Description** | | --- | --- | --- | -| PagerDuty.Notification_rules.start_delay_in_minutes | string | The delay time for notifying the user | -| PagerDuty.Notification_rules.urgency | string | The urgency of the notification | -| PagerDuty.Notification_rules.id | string | The id of the notification rule | +| PagerDuty.Notification_rules.start_delay_in_minutes | string | The delay time for notifying the user. | +| PagerDuty.Notification_rules.urgency | string | The urgency of the notification. | +| PagerDuty.Notification_rules.id | string | The id of the notification rule. | #### Command Example + ```!PagerDuty-get-users-notification UserID=someid``` #### Context Example + ```json { "PagerDuty": { @@ -537,39 +562,42 @@ Get the users notification rules #### Human Readable Output >### User notification rules + >|ID|Type|Urgency|Notification timeout(minutes)| >|---|---|---|---| >| someid | assignment_notification_rule | high | 0 | ### PagerDuty-resolve-event -*** -Resolves an existing event in PagerDuty +*** +Resolves an existing event in PagerDuty. #### Base Command `PagerDuty-resolve-event` + #### Input | **Argument Name** | **Description** | **Required** | | --- | --- | --- | | incident_key | Incident key. | Required | -| serviceKey | Service key for the integration. | Required | - +| serviceKey | Service key for the integration. | Optional | #### Context Output | **Path** | **Type** | **Description** | | --- | --- | --- | -| PagerDuty.Event.Status | string | Status of the action on the event | -| PagerDuty.Event.incident_key | string | Incident key | +| PagerDuty.Event.Status | string | Status of the action on the event. | +| PagerDuty.Event.incident_key | string | Incident key. | #### Command Example + ```!PagerDuty-resolve-event incident_key=somekey serviceKey=servicekey``` #### Context Example + ```json { "Event": { @@ -588,39 +616,42 @@ Resolves an existing event in PagerDuty #### Human Readable Output >### Resolve Event + >|Incident key|Message|Status| >|---|---|---| >| somekey | Event processed | success | ### PagerDuty-acknowledge-event -*** -Acknowledges an existing event in PagerDuty +*** +Acknowledges an existing event in PagerDuty. #### Base Command `PagerDuty-acknowledge-event` + #### Input | **Argument Name** | **Description** | **Required** | | --- | --- | --- | | incident_key | Incident key. | Required | -| serviceKey | Service key for the integration. | Required | - +| serviceKey | Service key for the integration. | Optional | #### Context Output | **Path** | **Type** | **Description** | | --- | --- | --- | -| PagerDuty.Event.Status | string | Status of the action on the event | -| PagerDuty.Event.incident_key | string | Incident key | +| PagerDuty.Event.Status | string | Status of the action on the event. | +| PagerDuty.Event.incident_key | string | Incident key. | #### Command Example + ```!PagerDuty-acknowledge-event incident_key=somekey serviceKey=servicekey``` #### Context Example + ```json { "Event": { @@ -639,60 +670,63 @@ Acknowledges an existing event in PagerDuty #### Human Readable Output >### Acknowledge Event + >|Incident key|Message|Status| >|---|---|---| >| somekey | Event processed | success | ### PagerDuty-get-incident-data -*** -Get data about a incident from PagerDuty +*** +Get data about a incident from PagerDuty. #### Base Command `PagerDuty-get-incident-data` + #### Input | **Argument Name** | **Description** | **Required** | | --- | --- | --- | | incident_id | ID of the incident to get information for. | Required | - #### Context Output | **Path** | **Type** | **Description** | | --- | --- | --- | -| PagerDuty.Incidents.ID | string | Incident ID | -| PagerDuty.Incidents.Title | string | The title of the incident | -| PagerDuty.Incidents.Status | string | Incident Status | -| PagerDuty.Incidents.created_at | date | Time in which the incident was created | -| PagerDuty.Incidents.urgency | string | Incident Urgency | -| PagerDuty.Incidents.assignee | string | The assignee of the incident | -| PagerDuty.Incidents.service_id | string | The id of the impacted service | -| PagerDuty.Incidents.service_name | string | The name of the impacted service | -| PagerDuty.Incidents.escalation_policy | string | The escalation policy | -| PagerDuty.Incidents.last_status_change_at | date | Time in which the last status change occurred | -| PagerDuty.Incidents.last_status_change_by | string | Name of the user who done the last status change | -| PagerDuty.Incidents.number_of_escalations | number | Number of escalations that took place | -| PagerDuty.Incidents.resolved_by | string | Name of the User who resolved the incident | -| PagerDuty.Incidents.resolve_reason | string | The reason for resolving the issue | -| PagerDuty.Incidents.Description | string | The Description of the incident | +| PagerDuty.Incidents.ID | string | Incident ID. | +| PagerDuty.Incidents.Title | string | The title of the incident. | +| PagerDuty.Incidents.Status | string | Incident Status. | +| PagerDuty.Incidents.created_at | date | Time in which the incident was created. | +| PagerDuty.Incidents.urgency | string | Incident Urgency. | +| PagerDuty.Incidents.assignee | string | The assignee of the incident. | +| PagerDuty.Incidents.service_id | string | The id of the impacted service. | +| PagerDuty.Incidents.service_name | string | The name of the impacted service. | +| PagerDuty.Incidents.escalation_policy | string | The escalation policy. | +| PagerDuty.Incidents.last_status_change_at | date | Time in which the last status change occurred. | +| PagerDuty.Incidents.last_status_change_by | string | Name of the user who done the last status change. | +| PagerDuty.Incidents.number_of_escalations | number | Number of escalations that took place. | +| PagerDuty.Incidents.resolved_by | string | Name of the User who resolved the incident. | +| PagerDuty.Incidents.resolve_reason | string | The reason for resolving the issue. | +| PagerDuty.Incidents.Description | string | The Description of the incident. | | PagerDuty.Incidents.teams.ID | string | The ID of the team assigned for the incident. | | PagerDuty.Incidents.teams.ID | string | The name of the team assigned for the incident. | -| PagerDuty.Incidents.assignment.time | date | The time of the assignment to the incident | -| PagerDuty.Incidents.assignment.assignee | string | The name of the assignee to the incident | -| PagerDuty.Incidents.assignment.assigneeId | string | The ID of the assignee to the incident | -| PagerDuty.Incidents.acknowledgement.time | date | The time of the acknowledgement to the incident | -| PagerDuty.Incidents.acknowledgement.acknowledger | string | The name of the acknowledger to the incident | -| PagerDuty.Incidents.acknowledgement.acknowledgerId | string | The ID of the acknowledger to the incident | -| PagerDuty.Incidents.incident_key | String | The incident's de-duplication key | +| PagerDuty.Incidents.assignment.time | date | The time of the assignment to the incident. | +| PagerDuty.Incidents.assignment.assignee | string | The name of the assignee to the incident. | +| PagerDuty.Incidents.assignment.assigneeId | string | The ID of the assignee to the incident. | +| PagerDuty.Incidents.acknowledgement.time | date | The time of the acknowledgement to the incident. | +| PagerDuty.Incidents.acknowledgement.acknowledger | string | The name of the acknowledger to the incident. | +| PagerDuty.Incidents.acknowledgement.acknowledgerId | string | The ID of the acknowledger to the incident. | +| PagerDuty.Incidents.incident_key | String | The incident's de-duplication key. | #### Command Example + ```!PagerDuty-get-incident-data incident_id=someid``` #### Context Example + ```json { "PagerDuty": { @@ -732,19 +766,21 @@ Get data about a incident from PagerDuty #### Human Readable Output >### PagerDuty Incident + >|ID|Title|Status|Created On|Urgency|Html Url|Incident key|Service ID|Service Name|Escalation Policy|Last Status Change On|Last Status Change By|Resolved By User| >|---|---|---|---|---|---|---|---|---|---|---|---|---| ->| someid | [#281] my new event | acknowledged | 2021-03-10T09:31:48Z | high | https://demisto.pagerduty.com/incidents/someid | 8e42eeb6391a4a2abeda5d12e09bddec | someid | API Service | Default | 2021-03-10T10:00:50Z | API Service | - | +>| someid | [#281] my new event | acknowledged | 2021-03-10T09:31:48Z | high | | 8e42eeb6391a4a2abeda5d12e09bddec | someid | API Service | Default | 2021-03-10T10:00:50Z | API Service | - | ### PagerDuty-get-service-keys -*** -Get Service keys for each of the services configured in the PagerDuty instance +*** +Get Service keys for each of the services configured in the PagerDuty instance. #### Base Command `PagerDuty-get-service-keys` + #### Input There are no input arguments for this command. @@ -753,19 +789,21 @@ There are no input arguments for this command. | **Path** | **Type** | **Description** | | --- | --- | --- | -| PagerDuty.Service.ID | string | The ID of the service connected to PagerDuty | -| PagerDuty.Service.Name | string | The name of the service connected to PagerDuty | -| PagerDuty.Service.Status | string | The status of the service connected to PagerDuty | -| PagerDuty.Service.CreatedAt | date | The date in which the service connected to PagerDuty was created | -| PagerDuty.Service.Integration.Name | string | The name of the integration used with the service | -| PagerDuty.Service.Integration.Vendor | string | The name of the vendor for the integration used with the service.\(A value of 'Missing Vendor information' will appear once no information could be found\) | -| PagerDuty.Service.Integration.Key | string | The key used to control events with the integration | +| PagerDuty.Service.ID | string | The ID of the service connected to PagerDuty. | +| PagerDuty.Service.Name | string | The name of the service connected to PagerDuty. | +| PagerDuty.Service.Status | string | The status of the service connected to PagerDuty. | +| PagerDuty.Service.CreatedAt | date | The date in which the service connected to PagerDuty was created. | +| PagerDuty.Service.Integration.Name | string | The name of the integration used with the service. | +| PagerDuty.Service.Integration.Vendor | string | The name of the vendor for the integration used with the service.\(A value of 'Missing Vendor information' will appear once no information could be found\). | +| PagerDuty.Service.Integration.Key | string | The key used to control events with the integration. | #### Command Example + ```!PagerDuty-get-service-keys``` #### Context Example + ```json { "PagerDuty": { @@ -789,39 +827,46 @@ There are no input arguments for this command. ``` #### Human Readable Output + >### Service List + >|ID|Name|Status|Created At|Integration| >|---|---|---|---|---| >| someid | API Service | critical | 2016-03-20T14:00:55+02:00 | Name: API Service, Vendor: Missing Vendor information, Key: somekey
| ### PagerDuty-add-responders + *** -Add responders to an incident +Add responders to an incident. #### Base Command `PagerDuty-add-responders` + #### Input -| **Argument Name** | **Description** | **Required** | -| --- |----------------------------------------------------------------------------------| --- | -| incident_id | PagerDuty Incident ID to add responders to | Required | -| requestor_id | UserID sending the request (if blank, uses the default for the integration) | Required | -| message | Message to send to responders | Optional | -| user_requests | Comma separated list of User IDs to request response from | Optional | -| escalation_policy_requests | Comma separated list of Escalation Policy IDs to request response from | Optional | + +| **Argument Name** | **Description** | **Required** | +| --- | --- | --- | +| incident_id | PagerDuty Incident ID to add responders to. | Required | +| message | Message to send to responders. | Required | +| user_requests | Comma separated list of User IDs to request response from. | Optional | +| escalation_policy_requests | Comma separated list of Escalation Policy IDs to request response from. | Optional | +| requestor_id | UserID sending the request (if blank, uses the default for the integration). | Optional | #### Command Example + ```!PagerDuty-add-responders incident_id=PXP12GZ UserRequests=P09TT3C,PAIXXX Message="Please join zoom meeting" ``` #### Context Output -| **Path** | **Type** | **Description** | -| --- | --- |-----------------------| -| PagerDuty.ResponderRequests.ResponderID | string | ID of the Responder | -| PagerDuty.ResponderRequests.ResponderName | string | Name of the Responder | +| **Path** | **Type** | **Description** | +| --- | --- | --- | +| PagerDuty.ResponderRequests.ResponderID | String | The user ID of the responder added. | +| PagerDuty.ResponderRequests.ResponderName | String | The name of the responder added. | #### Context Example + ```json { "PagerDuty": @@ -854,8 +899,9 @@ Add responders to an incident ### PagerDuty-run-response-play + *** -Run a specified response play on a given incident. +Run a response play on PagerDuty (based on its UUID). Response Plays are a package of Incident Actions that can be applied during an Incident's life cycle. @@ -863,17 +909,21 @@ Response Plays are a package of Incident Actions that can be applied during an I #### Base Command `PagerDuty-run-response-play` + #### Input -| **Argument Name** | **Description** | **Required** | -| --- |-----------------------------------------------------------------------------------|--------------| -| incident_id | PagerDuty Incident ID targeted to run the response play | Required | -| from_email | The email address of a valid user associated with the account making the request. | Required | -| response_play_uuid | The response play ID of the response play associated with the request. | required | + +| **Argument Name** | **Description** | **Required** | +| --- | --- | --- | +| incident_id | The PagerDuty incident ID to run the play on. | Required | +| from_email | User's email to trigger the response play from. | Required | +| response_play_uuid | The UUID of the response play to run. | Required | #### Command Example + ```!PagerDuty-run-response-play incident_id="Q107XAAAAMBBR" from_email="john.doe@example.com" response_play_uuid="111111-88bb-bb37-181d-11111111110dewsq"``` >#### Human Readable Output + >Response play successfully run to the incident Q107XAAAAMBBR by john.doe@example.com diff --git a/Packs/PagerDuty/Integrations/PagerDuty/test_data/incidents.json b/Packs/PagerDuty/Integrations/PagerDuty/test_data/incidents.json new file mode 100644 index 000000000000..9ee7d99770b6 --- /dev/null +++ b/Packs/PagerDuty/Integrations/PagerDuty/test_data/incidents.json @@ -0,0 +1,254 @@ +{ + "incidents": [ + { + "incident_number": 1578, + "title": "this is a test", + "description": "this is a test", + "created_at": "2023-11-19T05:51:15Z", + "updated_at": "2023-11-19T05:51:28Z", + "status": "resolved", + "incident_key": "", + "service": { + "id": "", + "type": "service_reference", + "summary": "API Service", + "self": "https://api.pagerduty.com/test", + "html_url": "https://api.pagerduty.com/test" + }, + "assignments": [], + "assigned_via": "escalation_policy", + "last_status_change_at": "2023-11-19T05:51:28Z", + "resolved_at": "2023-11-19T05:51:28Z", + "first_trigger_log_entry": { + "id": "", + "type": "trigger_log_entry", + "summary": "Triggered through the API.", + "self": "https://api.pagerduty.com/test", + "html_url": "https://api.pagerduty.com/test", + "created_at": "2023-11-19T05:51:14Z", + "agent": { + "id": "", + "type": "aa", + "summary": "API", + "self": "https://api.pagerduty.com/test", + "html_url": "https://api.pagerduty.com/test" + }, + "channel": { + "type": "api", + "service_key": "test", + "description": "this is a test", + "incident_key": "test", + "details": { + "description": "No description" + }, + "cef_details": { + "client": null, + "client_url": null, + "contexts": null, + "creation_time": null, + "dedup_key": "test", + "description": "this is a test", + "details": { + "description": "No description" + }, + "event_class": "", + "message": "this is a test", + "mutations": [], + "priority": null, + "reporter_component": null, + "reporter_location": null, + "service_group": "", + "severity": "info", + "source_component": "", + "source_location": null, + "source_origin": "test", + "urgency": null, + "version": "1.0" + }, + "summary": "this is a test" + }, + "service": { + "id": "", + "type": "service_reference", + "summary": "API Service", + "self": "https://api.pagerduty.com/test", + "html_url": "https://api.pagerduty.com/test" + }, + "incident": { + "id": "", + "type": "incident_reference", + "summary": "[#] this is a test", + "self": "https://api.pagerduty.com/test", + "html_url": "https://api.pagerduty.com/test" + }, + "teams": [], + "contexts": [], + "event_details": { + "description": "this is a test" + } + }, + "alert_counts": { + "all": 0, + "triggered": 0, + "resolved": 0 + }, + "is_mergeable": false, + "escalation_policy": { + "id": "", + "type": "", + "summary": "Default", + "self": "https://api.pagerduty.com/test", + "html_url": "https://api.pagerduty.com/test" + }, + "teams": [], + "pending_actions": [], + "acknowledgements": [], + "basic_alert_grouping": null, + "alert_grouping": null, + "last_status_change_by": { + "id": "", + "type": "service_reference", + "summary": "API Service", + "self": "https://api.pagerduty.com/test", + "html_url": "https://api.pagerduty.com/test" + }, + "resolve_reason": null, + "incidents_responders": [], + "responder_requests": [], + "subscriber_requests": [], + "urgency": "high", + "id": "", + "type": "incident", + "summary": "[#] this is a test", + "self": "https://api.pagerduty.com/test", + "html_url": "https://api.pagerduty.com/test" + }, + { + "incident_number": 1579, + "title": "this is a test", + "description": "this is a test", + "created_at": "2023-11-19T05:52:03Z", + "updated_at": "2023-11-19T05:52:16Z", + "status": "resolved", + "incident_key": "", + "service": { + "id": "", + "type": "service_reference", + "summary": "API Service", + "self": "https://api.pagerduty.com/test", + "html_url": "https://api.pagerduty.com/test" + }, + "assignments": [], + "assigned_via": "escalation_policy", + "last_status_change_at": "2023-11-19T05:52:16Z", + "resolved_at": "2023-11-19T05:52:16Z", + "first_trigger_log_entry": { + "id": "", + "type": "trigger_log_entry", + "summary": "Triggered through the API.", + "self": "https://api.pagerduty.com/test", + "html_url": "https://api.pagerduty.com/test", + "created_at": "2023-11-19T05:52:03Z", + "agent": { + "id": "", + "type": "generic_events_api_inbound_integration_reference", + "summary": "API", + "self": "https://api.pagerduty.com/test", + "html_url": "https://api.pagerduty.com/test" + }, + "channel": { + "type": "api", + "service_key": "test", + "description": "this is a test", + "incident_key": "", + "details": { + "description": "No description" + }, + "cef_details": { + "client": null, + "client_url": null, + "contexts": null, + "creation_time": null, + "dedup_key": "", + "description": "this is a test", + "details": { + "description": "No description" + }, + "event_class": "", + "message": "this is a test", + "mutations": [], + "priority": null, + "reporter_component": null, + "reporter_location": null, + "service_group": "", + "severity": "info", + "source_component": "", + "source_location": null, + "source_origin": "test", + "urgency": null, + "version": "1.0" + }, + "summary": "this is a test" + }, + "service": { + "id": "", + "type": "service_reference", + "summary": "API Service", + "self": "https://api.pagerduty.com/test", + "html_url": "https://api.pagerduty.com/test" + }, + "incident": { + "id": "", + "type": "incident_reference", + "summary": "[#] this is a test", + "self": "https://api.pagerduty.com/test", + "html_url": "https://api.pagerduty.com/test" + }, + "teams": [], + "contexts": [], + "event_details": { + "description": "this is a test" + } + }, + "alert_counts": { + "all": 0, + "triggered": 0, + "resolved": 0 + }, + "is_mergeable": false, + "escalation_policy": { + "id": "", + "type": "test", + "summary": "Default", + "self": "https://api.pagerduty.com/test", + "html_url": "https://api.pagerduty.com/test" + }, + "teams": [], + "pending_actions": [], + "acknowledgements": [], + "basic_alert_grouping": null, + "alert_grouping": null, + "last_status_change_by": { + "id": "", + "type": "service_reference", + "summary": "API Service", + "self": "https://api.pagerduty.com/test", + "html_url": "https://api.pagerduty.com/test" + }, + "resolve_reason": null, + "incidents_responders": [], + "responder_requests": [], + "subscriber_requests": [], + "urgency": "high", + "id": "", + "type": "incident", + "summary": "[#] this is a test", + "self": "https://api.pagerduty.com/test", + "html_url": "https://api.pagerduty.com/test" + } + ], + "limit": 2, + "offset": 0, + "total": null, + "more": true +} \ No newline at end of file diff --git a/Packs/PagerDuty/ReleaseNotes/1_1_8.md b/Packs/PagerDuty/ReleaseNotes/1_1_8.md new file mode 100644 index 000000000000..279ad5aae00e --- /dev/null +++ b/Packs/PagerDuty/ReleaseNotes/1_1_8.md @@ -0,0 +1,9 @@ + +#### Integrations + +##### PagerDuty v2 + +- Updated the ***PagerDuty-incidents*** command to support pagination. +- Updated the ***PagerDuty-incidents*** command with multiple new arguments. +- Updated the Docker image to: *demisto/python3:3.10.13.81631*. + \ No newline at end of file diff --git a/Packs/PagerDuty/pack_metadata.json b/Packs/PagerDuty/pack_metadata.json index 8cc6260f3ff7..5a69ec305f5f 100644 --- a/Packs/PagerDuty/pack_metadata.json +++ b/Packs/PagerDuty/pack_metadata.json @@ -2,7 +2,7 @@ "name": "PagerDuty", "description": "Alert and notify users using PagerDuty", "support": "xsoar", - "currentVersion": "1.1.7", + "currentVersion": "1.1.8", "author": "Cortex XSOAR", "url": "https://www.paloaltonetworks.com/cortex", "email": "",