From 47112e28524b24770dd84984bbeee3c9fc936b1d Mon Sep 17 00:00:00 2001 From: Shelly Tzohar <45915502+Shellyber@users.noreply.github.com> Date: Thu, 22 Jun 2023 15:57:40 +0300 Subject: [PATCH] Zendesk enhancement (#27101) * First commit * Added new command "zendesk-group-user-list" * Update release notes * Added html_comment to the create and update ticket + README updates * Updated rn * just a commit * Added another command + updated TPB + Updated incoming mapper * Added test + small fixes * Remove comment * flake8 * Fixed docs notes * Update docker * Bump pack from version Zendesk to 2.0.8. * Some CR comments * Fixed release notes version * Fixed release notes version * Fixed cr comments + updated image * Fix tests * - * - * Last comment --------- Co-authored-by: Content Bot --- .../classifier-Zendesk_-_Incoming_Mapper.json | 3 + .../Zendesk/Integrations/Zendeskv2/README.md | 230 +++++++++- .../Integrations/Zendeskv2/Zendeskv2.py | 138 +++++- .../Integrations/Zendeskv2/Zendeskv2.yml | 182 +++++++- .../Integrations/Zendeskv2/Zendeskv2_test.py | 58 ++- Packs/Zendesk/ReleaseNotes/2_1_0.md | 19 + .../Zendesk/TestPlaybooks/Zendesk_V2_TEST.yml | 410 +++++++++++++----- Packs/Zendesk/pack_metadata.json | 2 +- 8 files changed, 880 insertions(+), 162 deletions(-) create mode 100644 Packs/Zendesk/ReleaseNotes/2_1_0.md diff --git a/Packs/Zendesk/Classifiers/classifier-Zendesk_-_Incoming_Mapper.json b/Packs/Zendesk/Classifiers/classifier-Zendesk_-_Incoming_Mapper.json index 7bb1817e942f..161b2f20f1f1 100644 --- a/Packs/Zendesk/Classifiers/classifier-Zendesk_-_Incoming_Mapper.json +++ b/Packs/Zendesk/Classifiers/classifier-Zendesk_-_Incoming_Mapper.json @@ -86,6 +86,9 @@ }, "IncomingMirrorError": { "simple": "incomming_mirror_error" + }, + "Assignment Group": { + "simple": "group_id" } } } diff --git a/Packs/Zendesk/Integrations/Zendeskv2/README.md b/Packs/Zendesk/Integrations/Zendeskv2/README.md index 8f679ef2a605..dab7447aa425 100644 --- a/Packs/Zendesk/Integrations/Zendeskv2/README.md +++ b/Packs/Zendesk/Integrations/Zendeskv2/README.md @@ -389,29 +389,31 @@ Required permissions: Agents. `zendesk-ticket-create` #### Input -| **Argument Name** | **Description** | **Required** | -| --- | --- | --- | -| subject | The subject of this ticket. | Required | -| type | The type of this ticket. Possible values are: problem, incident, question, task. | Required | -| requester | The user who requested this ticket. | Required | -| assignee_email | The email address of the agent to assign the ticket to. | Optional | -| collaborators | Users to add as CC's when creating a ticket. | Optional | -| description | The ticket description. | Required | -| recipient | The original recipient email address of the ticket. | Optional | -| status | The state of the ticket. Possible values are: new, open, pending, hold, solved, closed. | Optional | -| priority | The urgency with which the ticket should be addressed. Possible values are: urgent, high, normal, low. Default is normal. | Optional | -| due_at | If this is a ticket of type "task" it has a due date. Due date format uses ISO 8601 format. | Optional | -| email_ccs | An array of agents or end users email CCs to add or delete from the ticket. Default is add.\nThe format is '562624,562625:put,example@example.com:delete'. | Optional | -| external_id | An ID you can use to link Zendesk Support tickets to local records. | Optional | -| forum_topic_id | The topic in the Zendesk Web portal this ticket originated from, if any. | Optional | -| followers | An array of agent followers to add or delete from the ticket. Default is add.\nThe format is '562624,562625:put,example@example.com:delete'. | Optional | -| group_id | The group this ticket is assigned to. | Optional | -| organization_id | The organization of the requester. | Optional | -| problem_id | For tickets of type "incident", the ID of the problem the incident is linked to. | Optional | -| tags | The tags applied to this ticket. | Optional | +| **Argument Name** | **Description** | **Required** | +|------------------------| --- | --- | +| subject | The subject of this ticket. | Required | +| type | The type of this ticket. Possible values are: problem, incident, question, task. | Required | +| requester | The user who requested this ticket. | Required | +| assignee_email | The email address of the agent to assign the ticket to. | Optional | +| collaborators | Users to add as CC's when creating a ticket. | Optional | +| description | The ticket description. | Required | +| recipient | The original recipient email address of the ticket. | Optional | +| status | The state of the ticket. Possible values are: new, open, pending, hold, solved, closed. | Optional | +| priority | The urgency with which the ticket should be addressed. Possible values are: urgent, high, normal, low. Default is normal. | Optional | +| due_at | If this is a ticket of type "task" it has a due date. Due date format uses ISO 8601 format. | Optional | +| email_ccs | An array of agents or end users email CCs to add or delete from the ticket. Default is add.\nThe format is '562624,562625:put,example@example.com:delete'. | Optional | +| external_id | An ID you can use to link Zendesk Support tickets to local records. | Optional | +| forum_topic_id | The topic in the Zendesk Web portal this ticket originated from, if any. | Optional | +| followers | An array of agent followers to add or delete from the ticket. Default is add.\nThe format is '562624,562625:put,example@example.com:delete'. | Optional | +| group_id | The group this ticket is assigned to. | Optional | +| organization_id | The organization of the requester. | Optional | +| problem_id | For tickets of type "incident", the ID of the problem the incident is linked to. | Optional | +| tags | The tags applied to this ticket. | Optional | | via_followup_source_id | The ID of a closed ticket when creating a follow-up ticket. | Optional | -| custom_fields | Custom fields for the ticket (this is a JSON-formatted argument, see: https://developer.zendesk.com/documentation/ticketing/managing-tickets/creating-and-updating-tickets#setting-custom-field-values). | Optional | -| brand_id | Enterprise only. The ID of the brand this ticket is associated with. | Optional | +| custom_fields | Custom fields for the ticket (this is a JSON-formatted argument, see: https://developer.zendesk.com/documentation/ticketing/managing-tickets/creating-and-updating-tickets#setting-custom-field-values). | Optional | +| brand_id | Enterprise only. The ID of the brand this ticket is associated with. | Optional | +| comment | A comment to add to the ticket. | Optional | +| html_comment | An HTML comment to add to the ticket. | Optional | #### Context Output @@ -477,6 +479,7 @@ Required permissions: Agents. | assignee_email | The email address of the agent to assign the ticket to. | Optional | | collaborators | Users to add as CCs when creating a ticket. | Optional | | comment | A comment to add to the ticket. | Optional | +| html_comment | An HTML comment to add to the ticket. | Optional | | recipient | The original recipient email address of the ticket. | Optional | | status | The state of the ticket. Possible values are: open, pending, hold, solved, closed. | Optional | | priority | The urgency with which the ticket should be addressed. Possible values are: urgent, high, normal, low. | Optional | @@ -788,3 +791,186 @@ To set up the mirroring: Newly fetched incidents will be mirrored in the chosen direction. However, this selection does not affect existing incidents. **Important Note:** To ensure the mirroring works as expected, mappers are required, both for incoming and outgoing, to map the expected fields in Cortex XSOAR and Zendesk v2. +### zendesk-group-user-list + +*** +Get group's users. +Allowed for: Admins, Agents and Light Agents. + +*Note*: In case the group_id does not exist, the command will return all users. + +#### Base Command + +`zendesk-group-user-list` + +#### Input + +| **Argument Name** | **Description** | **Required** | +| --- | --- | --- | +| group_id | The ID of a specific group. | Required | +| limit | Maximum number of results to return. Default is 50. | Optional | +| page_size | The page size (used for pagination). | Optional | +| page_number | The page number (used for pagination). | Optional | + +#### Context Output + +| **Path** | **Type** | **Description** | +| --- | --- | --- | +| Zendesk.UserGroup.active | Boolean | False if the user has been deleted. | +| Zendesk.UserGroup.alias | String | An alias displayed to end users. | +| Zendesk.UserGroup.created_at | Date | The time the user was created. | +| Zendesk.UserGroup.custom_role_id | Number | A custom role if the user is an agent on the Enterprise plan or above. | +| Zendesk.UserGroup.default_group_id | Number | The ID of the user's default group. | +| Zendesk.UserGroup.details | String | Any details you want to store about the user, such as an address. | +| Zendesk.UserGroup.email | String | The user's primary email address. | +| Zendesk.UserGroup.external_id | String | A unique identifier from another system. | +| Zendesk.UserGroup.iana_time_zone | String | The time zone for the user. | +| Zendesk.UserGroup.id | Number | The user's ID. | +| Zendesk.UserGroup.last_login_at | Date | The last time the user signed in to Zendesk Support. | +| Zendesk.UserGroup.locale | String | The user's locale. | +| Zendesk.UserGroup.locale_id | Number | The user's locale ID. | +| Zendesk.UserGroup.moderator | Boolean | Whether the user has forum moderation capabilities. | +| Zendesk.UserGroup.name | String | The user's name. | +| Zendesk.UserGroup.notes | String | Any notes you want to store about the user. | +| Zendesk.UserGroup.only_private_comments | Boolean | True if the user can only create private comments. | +| Zendesk.UserGroup.organization_id | Number | The ID of the user's organization. | +| Zendesk.UserGroup.phone | String | The user's primary phone number. | +| Zendesk.UserGroup.photo | String | A URL pointing to the user's profile picture. | +| Zendesk.UserGroup.report_csv | Boolean | Whether the user can access the CSV report on the Search tab of the Reporting page in the Support admin interface. | +| Zendesk.UserGroup.restricted_agent | Boolean | Whether the agent has any restrictions; false for admins and unrestricted agents, true for other agents. | +| Zendesk.UserGroup.role | String | The user's role. Possible values are "end-user", "agent", or "admin". | +| Zendesk.UserGroup.role_type | Number | The user's role type. | +| Zendesk.UserGroup.shared | Boolean | Whether the user is shared from a different Zendesk Support instance. | +| Zendesk.UserGroup.shared_agent | Boolean | Whether the user is a shared agent from a different Zendesk Support instance. | +| Zendesk.UserGroup.shared_phone_number | Boolean | Whether the phone number is shared. | +| Zendesk.UserGroup.signature | String | The user's signature. | +| Zendesk.UserGroup.suspended | Boolean | Whether the agent is suspended. Tickets from suspended users are also suspended, and these users cannot sign in to the end user portal. | +| Zendesk.UserGroup.tags | Unknown | The user's tags. Only present if your account has user tagging enabled. | +| Zendesk.UserGroup.ticket_restriction | Unknown | Specifies which tickets the user has access to. Possible values are: "organization", "groups", "assigned", "requested", null. | +| Zendesk.UserGroup.time_zone | String | The user's time zone. | +| Zendesk.UserGroup.two_factor_auth_enabled | Unknown | Whether two factor authentication is enabled. | +| Zendesk.UserGroup.updated_at | Date | The time the user was last updated. | +| Zendesk.UserGroup.url | String | The URL that points to the user's API record. | +| Zendesk.UserGroup.user_fields | Unknown | The user fields as shown in the Zendesk user interface. | +| Zendesk.UserGroup.verified | Boolean | Whether any of the user's identities is verified. | + +#### Command example +```!zendesk-group-user-list group_id=12345 limit=1``` +#### Context Example +```json +{ + "Zendesk": { + "UserGroup": [ + { + "active": true, + "alias": "", + "created_at": "2022-03-27T08:42:06Z", + "custom_role_id": 4678497517981, + "default_group_id": 4678483739805, + "details": "", + "email": "test@user.com", + "external_id": null, + "iana_time_zone": "Asia/Jerusalem", + "id": 1908275070333, + "last_login_at": "2023-05-31T11:13:41Z", + "locale": "en-US", + "locale_id": 1, + "moderator": true, + "name": "Admin", + "notes": "", + "only_private_comments": false, + "organization_id": 4678483740317, + "phone": null, + "photo": null, + "report_csv": true, + "restricted_agent": false, + "role": "admin", + "role_type": 4, + "shared": false, + "shared_agent": false, + "shared_phone_number": null, + "signature": "", + "suspended": false, + "tags": [], + "ticket_restriction": null, + "time_zone": "Asia/Jerusalem", + "two_factor_auth_enabled": null, + "updated_at": "2023-05-31T11:13:41Z", + "url": "https://some-url/api/v2/users/1908275070333.json", + "user_fields": {}, + "verified": true + } + ] + } +} +``` + +#### Human Readable Output + +>### Zendesk Group Users: +>|Id|Name|Email|Role|CreatedAt| +>|---|---|---|---|---| +>| 1908275070333 | Admin | test@user.com | admin | 2022-03-27T08:42:06Z | + +### zendesk-group-list + +*** +Get Zendesk groups. +Allowed for: Admins, Agents. + +#### Base Command + +`zendesk-group-list` + +#### Input + +| **Argument Name** | **Description** | **Required** | +| --- | --- | --- | +| limit | Maximum number of results to return. Default is 50. | Optional | +| page_size | The page size (used for pagination). | Optional | +| page_number | The page number (used for pagination). | Optional | + +#### Context Output + +| **Path** | **Type** | **Description** | +| --- | --- | --- | +| Zendesk.Group.created_at | Date | The time the group was created. | +| Zendesk.Group.default | Boolean | If the group is the default one for the account. | +| Zendesk.Group.deleted | Boolean | `true` if the group is deleted `false` otherwise. | +| Zendesk.Group.description | String | The description of the group. | +| Zendesk.Group.id | Number | The group ID. | +| Zendesk.Group.is_public | Boolean | If true, the group is public. If false, the group is private. You can't change a private group to a public group. | +| Zendesk.Group.name | String | The name of the group. | +| Zendesk.Group.updated_at | Date | The time of the last update of the group. | +| Zendesk.Group.url | String | The API URL of the group. | + +#### Command example +```!zendesk-group-list limit=1``` +#### Context Example +```json +{ + "Zendesk": { + "Group": [ + { + "created_at": "2023-06-06T07:44:20Z", + "default": false, + "deleted": false, + "description": "This is a group for testing", + "id": 11395818128925, + "is_public": true, + "name": "Test Group", + "updated_at": "2023-06-06T07:44:20Z", + "url": "https://some-url/api/v2/groups/11395818128925.json" + } + ] + } +} +``` + +#### Human Readable Output + +>### Zendesk groups: +>|Id| Name |IsPublic|CreatedAt|UpdatedAt| +>|------|---|---|---|---| +>| 11395818128925 | Test Group | true | 2023-06-06T07:44:20Z | 2023-06-06T07:44:20Z | + diff --git a/Packs/Zendesk/Integrations/Zendeskv2/Zendeskv2.py b/Packs/Zendesk/Integrations/Zendeskv2/Zendeskv2.py index 1422e576ae8c..855681ffe3b4 100644 --- a/Packs/Zendesk/Integrations/Zendeskv2/Zendeskv2.py +++ b/Packs/Zendesk/Integrations/Zendeskv2/Zendeskv2.py @@ -17,6 +17,8 @@ TICKETS_HEADERS = ['id', 'subject', 'description', 'priority', 'status', 'assignee_id', 'created_at', 'updated_at', 'external_id'] COMMENTS_HEADERS = ['id', 'body', 'created_at', 'public', 'attachments'] ATTACHMENTS_HEADERS = ['id', 'file_name', 'content_url', 'size', 'content_type'] +GROUP_USER_HEADERS = ['id', 'name', 'email', 'role', 'created_at'] +GROUP_HEADERS = ['id', 'name', 'is_public', 'created_at', 'updated_at'] ARTICLES_HEADERS = ['body'] ROLES = ['end-user', 'admin', 'agent'] ROLE_TYPES = { @@ -372,7 +374,7 @@ def _paged_request(self, url_suffix: str, data_field_name: str, params: Optional return self.__cursor_pagination(url_suffix=url_suffix, data_field_name=data_field_name, params=params, limit=int(limit)) - # ---- user releated functions ---- # + # ---- user related functions ---- # @staticmethod def __command_results_zendesk_users(users: List[Dict]): @@ -489,7 +491,7 @@ def zendesk_user_delete(self, user_id: str): # pragma: no cover self._http_request('DELETE', url_suffix=f'users/{user_id}') return f'User deleted. (id: {user_id})' - # ---- organization releated functions ---- # + # ---- organization related functions ---- # @staticmethod def __command_results_zendesk_organizations(organizations: List[Dict]): # pragma: no cover @@ -513,7 +515,30 @@ def zendesk_organization_list(self, organization_id: Optional[str] = None, **kwa return self.__command_results_zendesk_organizations(organizations) - # ---- ticket releated functions ---- # + # ---- group related functions ---- # + @staticmethod + def __command_results_zendesk_group_users(users: List[Dict]): # pragma: no cover + readable_outputs = tableToMarkdown(name='Zendesk Group Users:', t=users, headers=GROUP_USER_HEADERS, + headerTransform=camelize_string) + return CommandResults(outputs_prefix="Zendesk.UserGroup", + outputs=users, readable_output=readable_outputs) + + def list_group_users(self, group_id: int, **kwargs): + users = list(self._paged_request(url_suffix=f'groups/{group_id}/users', data_field_name='users', **kwargs)) + return self.__command_results_zendesk_group_users(users) + + @staticmethod + def __command_results_zendesk_groups(groups): # pragma: no cover + readable_outputs = tableToMarkdown(name='Zendesk groups:', t=groups, headers=GROUP_HEADERS, + headerTransform=camelize_string) + return CommandResults(outputs_prefix="Zendesk.Group", + outputs=groups, readable_output=readable_outputs) + + def list_groups(self, **kwargs): + groups = list(self._paged_request(url_suffix='groups', data_field_name='groups', **kwargs)) + return self.__command_results_zendesk_groups(groups) + + # ---- ticket related functions ---- # @staticmethod def __ticket_context(ticket: Dict[str, Any]): @@ -537,7 +562,7 @@ def _get_ticket_by_id(self, ticket_id: str, **kwargs): def __get_sort_params(sort: str, cursor_paging: bool = False): Validators.validate_ticket_sort(sort) if not cursor_paging: - # using the offest paged request + # using the offset paged request sort_list = sort.split('_') sort, order = '_'.join(sort_list[:-1]), sort_list[-1] return { @@ -600,7 +625,8 @@ def zendesk_ticket_list(self, ticket_id: Optional[STR_OR_STR_LIST] = None, query class Ticket: def __init__(self, type: Optional[str] = None, collaborators: Optional[str] = None, - comment: Optional[str] = None, public: Optional[Union[str, bool]] = None, + comment: Optional[str] = None, html_comment: Optional[str] = None, + public: Optional[Union[str, bool]] = None, email_ccs: Optional[str] = None, priority: Optional[str] = None, followers: Optional[Union[List[str], str]] = None, status: Optional[str] = None, **kwargs): @@ -619,6 +645,8 @@ def __init__(self, type: Optional[str] = None, collaborators: Optional[str] = No self._data['comment'] = {'body': comment} if public: self._data['comment']['public'] = argToBoolean(public) + if html_comment: + self._data['comment'] = {'html_body': html_comment} if status: Validators.validate_ticket_status(status) self._data['status'] = status @@ -708,7 +736,7 @@ def __command_results_zendesk_ticket_comments(comments: List[Dict]): return CommandResults(outputs_prefix="Zendesk.Ticket.Comment", outputs=comments, readable_output=readable_outputs) - def _get_comments(self, ticket_id: str, **kwargs): + def _get_comments(self, ticket_id: str, **kwargs) -> list: # type:ignore for comment in self._paged_request(url_suffix=f'tickets/{ticket_id}/comments', data_field_name='comments', **kwargs): for attachment in comment.get('attachments', []): attachment.pop('thumbnails', None) @@ -717,7 +745,7 @@ def _get_comments(self, ticket_id: str, **kwargs): def zendesk_ticket_comment_list(self, ticket_id: str, **kwargs): return self.__command_results_zendesk_ticket_comments(list(self._get_comments(ticket_id, **kwargs))) - # ---- attachment releated functions ---- # + # ---- attachment related functions ---- # def zendesk_ticket_attachment_add(self, file_id: STR_OR_STR_LIST, ticket_id: int, comment: str, file_name: Optional[STR_OR_STR_LIST] = None, is_mirror: bool = False): @@ -746,7 +774,7 @@ def zendesk_ticket_attachment_add(self, file_id: STR_OR_STR_LIST, ticket_id: int return f'file: {", ".join(uploaded_files)} attached to ticket: {ticket_id}' - def zendesk_attachment_get(self, attachment_id: int): + def zendesk_attachment_get(self, attachment_id): attachments = [ self._http_request( 'GET', @@ -759,18 +787,30 @@ def filter_thumbnails(attachment: Dict): return attachment attachments = list(map(filter_thumbnails, attachments)) - readable_output = tableToMarkdown(name='Zendesk attachments', t=attachments, - headers=ATTACHMENTS_HEADERS, headerTransform=camelize_string) - results = [CommandResults(outputs_prefix='Zendesk.Attachment', - outputs=attachments, readable_output=readable_output)] + + return attachments + + def get_file_entries(self, attachments): + results = [] for attachment_link, attachment_name in map(lambda x: (x['content_url'], x['file_name']), attachments): res = self._http_request('GET', full_url=attachment_link, resp_type='response') res.raise_for_status() results.append(fileResult(filename=attachment_name, data=res.content, file_type=EntryType.ENTRY_INFO_FILE)) + return results + + def zendesk_attachment_get_command(self, attachment_id: int): + attachments = self.zendesk_attachment_get(attachment_id) + readable_output = tableToMarkdown(name='Zendesk attachments', t=attachments, + headers=ATTACHMENTS_HEADERS, headerTransform=camelize_string) + results = [CommandResults(outputs_prefix='Zendesk.Attachment', + outputs=attachments, readable_output=readable_output)] + + file_entries = self.get_file_entries(attachments) + results.append(file_entries) return results - # ---- search releated functions ---- # + # ---- search related functions ---- # def __zendesk_search_results(self, query: str, limit: int = 50, page_number: Optional[int] = None, page_size: int = 50, additional_params: dict = {}): @@ -798,7 +838,7 @@ def zendesk_search(self, query: str, limit: int = 50, page_number: Optional[int] query=query, limit=limit, page_number=page_number, page_size=page_size )) - # ---- articles releated functions ---- # + # ---- articles related functions ---- # def zendesk_article_list(self, locale: Optional[str] = '', article_id: Optional[int] = None, **kwargs): if locale: @@ -818,7 +858,7 @@ def zendesk_article_list(self, locale: Optional[str] = '', article_id: Optional[ return CommandResults(outputs_prefix='Zendesk.Article', outputs=articles, readable_output='\n\n\n'.join(readable_output)) - # ---- demisto releated functions ---- # + # ---- demisto related functions ---- # def test_module(self): # pragma: no cover exception: Exception @@ -832,8 +872,7 @@ def test_module(self): # pragma: no cover raise exception from None - @staticmethod - def _ticket_to_incident(ticket: Dict): + def _ticket_to_incident(self, ticket: Dict): ticket |= { 'severity': PRIORITY_MAP.get(ticket['priority']), 'mirror_instance': INTEGRATION_INSTANCE, @@ -846,6 +885,7 @@ def _ticket_to_incident(ticket: Dict): 'rawJSON': json.dumps(ticket), 'name': ticket['subject'], 'occurred': ticket['created_at'], + 'attachment': ticket.get('attachments', []) } @staticmethod @@ -868,6 +908,7 @@ def _fetch_args(params, last_run): time_filter = 'updated' if params.get('time_filter') == 'updated-at' else 'created' query = params.get('fetch_query') or ZendeskClient._fetch_query_builder(**params) max_fetch = min(100, int(params.get('max_fetch') or 50)) + get_attachments = params.get('get_attachments') # from last_run fetched_tickets = deque(last_run.get('fetched_tickets') or []) @@ -883,7 +924,7 @@ def _fetch_args(params, last_run): raise DemistoException(f'invalid first fetch time specified ({first_fetch})') last_fetch = first_fetch_datetime.strftime(ZENDESK_FETCH_TIME_FORMAT) - return fetched_tickets, last_fetch, time_filter, query, max_fetch, page_number + return fetched_tickets, last_fetch, time_filter, query, max_fetch, page_number, get_attachments @staticmethod def _next_fetch_args(fetched_tickets, search_results_ids, next_run_start_time, query, time_filter, @@ -905,9 +946,54 @@ def _next_fetch_args(fetched_tickets, search_results_ids, next_run_start_time, q return next_run + def get_attachments_ids(self, ticket: dict) -> List[int]: + """ + + Args: + ticket (dict): The fetched ticket. + + Returns (list): all the attachment ids for a ticket + + """ + attachments_ids = [] + ticket_id = ticket['id'] + comments_list = self._get_comments(ticket_id=ticket_id) + for comment in comments_list: + attachments = comment.get('attachments', []) + for attachment in attachments: + attachment_id = attachment.get('id') + attachments_ids.append(attachment_id) + + return attachments_ids + + def get_attachment_entries(self, ticket: dict) -> list: + """ + + Args: + ticket (dict): The ticket to get the file entries + + Returns: The attachments entries. + + """ + attachments_ids = self.get_attachments_ids(ticket) + file_names = [] + attachments = self.zendesk_attachment_get(attachments_ids) + demisto.debug(f'The fetched attachments - {attachments}') + attachments_entries = self.get_file_entries(attachments) + if isinstance(attachments_entries, list): + for file_result in attachments_entries: + if file_result['Type'] == entryTypes['error']: + raise Exception(f"Error getting attachment: {str(file_result.get('Contents', ''))}") + file_names.append({ + 'path': file_result.get('FileID', ''), + 'name': file_result.get('File', '') + }) + return file_names + def fetch_incidents(self, params: dict, lastRun: Optional[str] = None): last_run = json.loads(lastRun or 'null') or demisto.getLastRun() or {} - fetched_tickets, last_fetch, time_filter, query, max_fetch, page_number = self._fetch_args(params, last_run) + fetched_tickets, last_fetch, time_filter, query, max_fetch, page_number, get_attachments = self._fetch_args(params, + last_run) # look back window for tickets next_run_start_time = datetime.utcnow() - timedelta(minutes=1) @@ -923,6 +1009,14 @@ def fetch_incidents(self, params: dict, lastRun: Optional[str] = None): search_results_ids = list(map(lambda x: x['id'], search_results)) filtered_search_results_ids = list(filter(lambda x: x not in fetched_tickets, search_results_ids)) tickets = map(lambda x: self._get_ticket_by_id(x), filtered_search_results_ids) + ticket_modified = [] + if get_attachments: + for ticket in tickets: + attachments = ZendeskClient.get_attachment_entries(self, ticket) + ticket.update({'attachments': attachments}) + ticket_modified.append(ticket) + + tickets = ticket_modified if ticket_modified else tickets incidents = list(map(self._ticket_to_incident, tickets)) demisto.incidents(incidents) @@ -1110,6 +1204,10 @@ def main(): # pragma: no cover 'zendesk-user-create': client.zendesk_user_create, 'zendesk-user-update': client.zendesk_user_update, 'zendesk-user-delete': client.zendesk_user_delete, + 'zendesk-group-user-list': client.list_group_users, + + # Group commands + 'zendesk-group-list': client.list_groups, # organization commands 'zendesk-organization-list': client.zendesk_organization_list, @@ -1123,7 +1221,7 @@ def main(): # pragma: no cover # attachment commands 'zendesk-ticket-attachment-add': client.zendesk_ticket_attachment_add, - 'zendesk-attachment-get': client.zendesk_attachment_get, + 'zendesk-attachment-get': client.zendesk_attachment_get_command, # search command 'zendesk-search': client.zendesk_search, diff --git a/Packs/Zendesk/Integrations/Zendeskv2/Zendeskv2.yml b/Packs/Zendesk/Integrations/Zendeskv2/Zendeskv2.yml index f8ca2a79e571..cc69ba60f414 100644 --- a/Packs/Zendesk/Integrations/Zendeskv2/Zendeskv2.yml +++ b/Packs/Zendesk/Integrations/Zendeskv2/Zendeskv2.yml @@ -45,6 +45,12 @@ configuration: additionalinfo: If true, XSOAR will mirror also the ticket closeing. hidden: - marketplacev2 +- display: Get incident attachments + name: get_attachments + required: false + type: 8 + section: Collect + advanced: true - additionalinfo: Comment and files that will be marked with this tag will be pushed into Zendesk. defaultvalue: mirror display: Mirror tags @@ -709,6 +715,174 @@ script: - contextPath: Zendesk.Organization.url type: string description: The API URL of this organization. + - name: zendesk-group-user-list + description: |- + Get group's users. + Allowed for: Admins, Agents and Light Agents. + arguments: + - name: group_id + required: true + description: |- + The ID of a specific group. + - name: limit + defaultValue: 50 + description: Maximum number of results to return. + - name: page_size + description: The page size (used for pagination). + - name: page_number + description: The page number (used for pagination). + outputs: + - contextPath: Zendesk.UserGroup.active + description: False if the user has been deleted. + type: Boolean + - contextPath: Zendesk.UserGroup.alias + description: An alias displayed to end users. + type: String + - contextPath: Zendesk.UserGroup.created_at + description: The time the user was created. + type: Date + - contextPath: Zendesk.UserGroup.custom_role_id + description: A custom role if the user is an agent on the Enterprise plan or above. + type: Number + - contextPath: Zendesk.UserGroup.default_group_id + description: The ID of the user's default group. + type: Number + - contextPath: Zendesk.UserGroup.details + description: Any details you want to store about the user, such as an address. + type: String + - contextPath: Zendesk.UserGroup.email + description: The user's primary email address. + type: String + - contextPath: Zendesk.UserGroup.external_id + description: A unique identifier from another system. + type: String + - contextPath: Zendesk.UserGroup.iana_time_zone + description: The time zone for the user. + type: String + - contextPath: Zendesk.UserGroup.id + description: The user's ID. + type: Number + - contextPath: Zendesk.UserGroup.last_login_at + description: The last time the user signed in to Zendesk Support. + type: Date + - contextPath: Zendesk.UserGroup.locale + description: The user's locale. + type: String + - contextPath: Zendesk.UserGroup.locale_id + description: The user's locale ID. + type: Number + - contextPath: Zendesk.UserGroup.moderator + description: Whether the user has forum moderation capabilities. + type: Boolean + - contextPath: Zendesk.UserGroup.name + description: The user's name. + type: String + - contextPath: Zendesk.UserGroup.notes + description: Any notes you want to store about the user. + type: String + - contextPath: Zendesk.UserGroup.only_private_comments + description: True if the user can only create private comments. + type: Boolean + - contextPath: Zendesk.UserGroup.organization_id + description: The ID of the user's organization. + type: Number + - contextPath: Zendesk.UserGroup.phone + description: The user's primary phone number. + type: String + - contextPath: Zendesk.UserGroup.photo + description: A URL pointing to the user's profile picture. + type: String + - contextPath: Zendesk.UserGroup.report_csv + description: Whether the user can access the CSV report on the Search tab of the Reporting page in the Support admin interface. + type: Boolean + - contextPath: Zendesk.UserGroup.restricted_agent + description: Whether the agent has any restrictions; false for admins and unrestricted agents, true for other agents. + type: Boolean + - contextPath: Zendesk.UserGroup.role + description: The user's role. Possible values are "end-user", "agent", or "admin". + type: String + - contextPath: Zendesk.UserGroup.role_type + description: The user's role type. + type: Number + - contextPath: Zendesk.UserGroup.shared + description: Whether the user is shared from a different Zendesk Support instance. + type: Boolean + - contextPath: Zendesk.UserGroup.shared_agent + description: Whether the user is a shared agent from a different Zendesk Support instance. + type: Boolean + - contextPath: Zendesk.UserGroup.shared_phone_number + description: Whether the phone number is shared. + type: Boolean + - contextPath: Zendesk.UserGroup.signature + description: The user's signature. + type: String + - contextPath: Zendesk.UserGroup.suspended + description: Whether the agent is suspended. Tickets from suspended users are also suspended, and these users cannot sign in to the end user portal. + type: Boolean + - contextPath: Zendesk.UserGroup.tags + description: The user's tags. Only present if your account has user tagging enabled. + type: Unknown + - contextPath: Zendesk.UserGroup.ticket_restriction + description: 'Specifies which tickets the user has access to. Possible values are: "organization", "groups", "assigned", "requested", null.' + type: Unknown + - contextPath: Zendesk.UserGroup.time_zone + description: The user's time zone. + type: String + - contextPath: Zendesk.UserGroup.two_factor_auth_enabled + description: Whether two factor authentication is enabled. + type: Unknown + - contextPath: Zendesk.UserGroup.updated_at + description: The time the user was last updated. + type: Date + - contextPath: Zendesk.UserGroup.url + description: The URL that points to the user's API record. + type: String + - contextPath: Zendesk.UserGroup.user_fields + description: The user fields as shown in the Zendesk user interface. + type: Unknown + - contextPath: Zendesk.UserGroup.verified + description: Whether any of the user's identities is verified. + type: Boolean + - name: zendesk-group-list + description: |- + Get Zendesk groups. + Allowed for: Admins, Agents. + arguments: + - name: limit + default: true + description: Maximum number of results to return. + - name: page_size + description: The page size (used for pagination). + - name: page_number + description: The page number (used for pagination). + outputs: + - contextPath: Zendesk.Group.created_at + description: The time the group was created. + type: Date + - contextPath: Zendesk.Group.default + description: If the group is the default one for the account. + type: Boolean + - contextPath: Zendesk.Group.deleted + description: true if the group is deleted false otherwise. + type: Boolean + - contextPath: Zendesk.Group.description + description: The description of the group. + type: String + - contextPath: Zendesk.Group.id + description: The group ID. + type: Number + - contextPath: Zendesk.Group.is_public + description: If true, the group is public. If false, the group is private. You can't change a private group to a public group + type: Boolean + - contextPath: Zendesk.Group.name + description: The name of the group. + type: String + - contextPath: Zendesk.Group.updated_at + description: The time of the last update of the group. + type: Date + - contextPath: Zendesk.Group.url + description: The API URL of the group. + type: String - name: zendesk-ticket-list description: |- List Zendesk tickets. @@ -883,6 +1057,10 @@ script: description: The user who requested this ticket. - name: assignee_email description: The email address of the agent to assign the ticket to. + - name: comment + description: A comment to add to the ticket. + - name: html_comment + description: An HTML comment to add to the ticket. - name: collaborators isArray: true description: Users to add as CC's when creating a ticket. @@ -1078,6 +1256,8 @@ script: description: Users to add as CCs when creating a ticket. - name: comment description: A comment to add to the ticket. + - name: html_comment + description: An HTML comment to add to the ticket. - name: recipient description: The original recipient email address of the ticket. - name: status @@ -1411,7 +1591,7 @@ script: description: Updates local incident changes in the remote incident. This method is only used for debugging purposes and will not update the current incident. execution: false name: update-remote-system - dockerimage: demisto/python3:3.10.11.54132 + dockerimage: demisto/python3:3.10.12.63474 isfetch: true runonce: false subtype: python3 diff --git a/Packs/Zendesk/Integrations/Zendeskv2/Zendeskv2_test.py b/Packs/Zendesk/Integrations/Zendeskv2/Zendeskv2_test.py index 09a1300e2104..03e10a83a489 100644 --- a/Packs/Zendesk/Integrations/Zendeskv2/Zendeskv2_test.py +++ b/Packs/Zendesk/Integrations/Zendeskv2/Zendeskv2_test.py @@ -582,6 +582,12 @@ def test_zendesk_organization_list_general(self, zendesk_client, mocker): zendesk_client.zendesk_organization_list() mock_zendesk_organization_general.assert_called_once() + def test_zendesk_group_users_list_general(self, zendesk_client, mocker): + mock_zendesk_organization_general = mocker.patch.object(zendesk_client, '_paged_request') + mocker.patch.object(zendesk_client, '_ZendeskClient__command_results_zendesk_group_users') + zendesk_client.list_group_users(group_id=100) + mock_zendesk_organization_general.assert_called_once() + class TestTicketList: def test_with_ticket_id(self, zendesk_client, requests_mock): ticket_mock = requests_mock.get(full_url('tickets/10'), json=get_json_file('tickets/10')) @@ -765,7 +771,8 @@ def test_fetch_query_builder(self, args, expected_outputs): @pytest.mark.parametrize(test_fetch_args_parametrize_str, data_test_fetch_args) def test_fetch_args(self, params, last_run, expected_fetched_tickets, expected_last_fetch, expected_time_filter, expected_query, expected_max_fetch, expected_page_number): - fetched_tickets, last_fetch, time_filter, query, max_fetch, page_number = ZendeskClient._fetch_args(params, last_run) + fetched_tickets, last_fetch, time_filter, query, max_fetch, page_number, get_attachments = \ + ZendeskClient._fetch_args(params, last_run) assert fetched_tickets == expected_fetched_tickets assert last_fetch == expected_last_fetch assert time_filter == expected_time_filter @@ -848,7 +855,10 @@ def test_initial_flow(self, mocker, zendesk_client, requests_mock): ticket_mock_10 = requests_mock.get(full_url('tickets/10'), json=get_json_file('tickets/10')) ticket_mock_20 = requests_mock.get(full_url('tickets/20'), json=get_json_file('tickets/20')) mocker.patch.object(demisto, 'getLastRun', return_value=None) - mocker.patch.object(zendesk_client, '_ZendeskClient__zendesk_search_results', return_value=[{'id': 10}, {'id': 20}]) + mocker.patch.object(zendesk_client, '_ZendeskClient__zendesk_search_results', + return_value=[{'id': 10}, {'id': 20}]) + mocker.patch.object(zendesk_client, '_get_comments', return_value=[]) + mocker.patch.object(zendesk_client, 'get_attachment_entries', return_value=[]) demisto_incidents_mock = mocker.patch.object(demisto, 'incidents') demisto_set_lust_run_mock = mocker.patch.object(demisto, 'setLastRun') zendesk_client.fetch_incidents({}, {}) @@ -865,6 +875,8 @@ def test_continues_fetch_first_part(self, mocker, zendesk_client, requests_mock) ticket_mock_20 = requests_mock.get(full_url('tickets/20'), json=get_json_file('tickets/20')) mocker.patch.object(demisto, 'getLastRun', return_value=None) mocker.patch.object(zendesk_client, '_ZendeskClient__zendesk_search_results', return_value=[{'id': 10}]) + mocker.patch.object(zendesk_client, '_get_comments', return_value=[]) + mocker.patch.object(zendesk_client, 'get_attachment_entries', return_value=[]) demisto_incidents_mock = mocker.patch.object(demisto, 'incidents') demisto_set_lust_run_mock = mocker.patch.object(demisto, 'setLastRun') zendesk_client.fetch_incidents({'max_fetch': 1}, {}) @@ -884,6 +896,8 @@ def test_continues_fetch_second_part(self, mocker, zendesk_client, requests_mock ticket_mock_20 = requests_mock.get(full_url('tickets/20'), json=get_json_file('tickets/20')) mocker.patch.object(demisto, 'getLastRun', return_value=None) mocker.patch.object(zendesk_client, '_ZendeskClient__zendesk_search_results', return_value=[{'id': 20}]) + mocker.patch.object(zendesk_client, '_get_comments', return_value=[]) + mocker.patch.object(zendesk_client, 'get_attachment_entries', return_value=[]) demisto_incidents_mock = mocker.patch.object(demisto, 'incidents') demisto_set_lust_run_mock = mocker.patch.object(demisto, 'setLastRun') zendesk_client.fetch_incidents( @@ -910,6 +924,8 @@ def test_continues_fetch_last_part(self, mocker, zendesk_client, requests_mock): ticket_mock_20 = requests_mock.get(full_url('tickets/20'), json=get_json_file('tickets/20')) mocker.patch.object(demisto, 'getLastRun', return_value=None) mocker.patch.object(zendesk_client, '_ZendeskClient__zendesk_search_results', return_value=[]) + mocker.patch.object(zendesk_client, '_get_comments', return_value=[]) + mocker.patch.object(zendesk_client, 'get_attachment_entries', return_value=[]) demisto_incidents_mock = mocker.patch.object(demisto, 'incidents') demisto_set_lust_run_mock = mocker.patch.object(demisto, 'setLastRun') zendesk_client.fetch_incidents({'max_fetch': 1}, json.dumps({ @@ -927,6 +943,8 @@ def test_usual_fetch(self, mocker, zendesk_client, requests_mock): ticket_mock_20 = requests_mock.get(full_url('tickets/20'), json=get_json_file('tickets/20')) mocker.patch.object(demisto, 'getLastRun', return_value=None) mocker.patch.object(zendesk_client, '_ZendeskClient__zendesk_search_results', return_value=[{'id': 20}]) + mocker.patch.object(zendesk_client, '_get_comments', return_value=[]) + mocker.patch.object(zendesk_client, 'get_attachment_entries', return_value=[]) demisto_incidents_mock = mocker.patch.object(demisto, 'incidents') demisto_set_lust_run_mock = mocker.patch.object(demisto, 'setLastRun') zendesk_client.fetch_incidents({}, json.dumps({ @@ -940,3 +958,39 @@ def test_usual_fetch(self, mocker, zendesk_client, requests_mock): assert list(map(lambda x: json.loads(x['rawJSON'])['id'], demisto_incidents_mock.call_args[0][0])) == [20] assert demisto_set_lust_run_mock.call_args[0][0] == { 'fetched_tickets': [10, 20], 'fetch_time': '2023-01-15T11:59:00Z'} + + @freeze_time('2023-01-15T12:00:00Z') + def test_usual_fetch_with_attachment(self, mocker, zendesk_client, requests_mock): + fetched_args = ([10], '2023-01-12T12:00:00Z', '2023-01-12T12:00:00Z', '', 50, 1, True) + ticket_mock_10 = requests_mock.get(full_url('tickets/10'), json=get_json_file('tickets/10')) + ticket_mock_20 = requests_mock.get(full_url('tickets/20'), json=get_json_file('tickets/20')) + mocker.patch.object(zendesk_client, '_fetch_args', return_value=fetched_args) + mocker.patch.object(demisto, 'getLastRun', return_value=None) + mocker.patch.object(zendesk_client, '_ZendeskClient__zendesk_search_results', return_value=[{'id': 20}]) + mocker.patch.object(zendesk_client, 'get_attachments_ids', return_value=[1234]) + mocker.patch.object(zendesk_client, 'zendesk_attachment_get', + return_value={'url': 'testurl/api/v2/attachments/11656206786333.json', 'id': 11656206786333, + 'file_name': 'TestFile.json', + 'content_url': 'testurl/attachments/token/1234/?name=TestFile.json', + 'mapped_content_url': 'testurl/attachments/token/1234/?name=TestFile.json', + 'content_type': 'application/x-yaml', 'size': 44726, 'width': None, + 'height': None, 'inline': False, 'deleted': False, + 'malware_access_override': False, 'malware_scan_result': 'malware_not_found'}) + mocker.patch.object(zendesk_client, 'get_file_entries', return_value=[{'Contents': '', 'ContentsFormat': 'text', + 'Type': 9, 'File': 'TestFile.json', + 'FileID': '77fe1c6d-3096-4f1c-80c7-' + '4e7c8573d580'}]) + mocker.patch.object(zendesk_client, 'get_attachment_entries', + return_value=[{'path': '77fe1c6d-3096-4f1c-80c7-4e7c8573d580', + 'name': 'TestFile.json'}]) + demisto_incidents_mock = mocker.patch.object(demisto, 'incidents') + zendesk_client.fetch_incidents({}, json.dumps({ + "fetched_tickets": [10], + "fetch_time": "2023-01-12T12:00:00Z" + })) + assert ticket_mock_10.call_count == 0 + assert ticket_mock_20.called_once + assert demisto_incidents_mock.called_once() + assert list(map(lambda x: json.loads(x['rawJSON'])['id'], demisto_incidents_mock.call_args[0][0])) == [20] + assert demisto_incidents_mock.call_args[0][0][0]['attachment'] == [{ + 'path': '77fe1c6d-3096-4f1c-80c7-4e7c8573d580', 'name': 'TestFile.json'}] diff --git a/Packs/Zendesk/ReleaseNotes/2_1_0.md b/Packs/Zendesk/ReleaseNotes/2_1_0.md new file mode 100644 index 000000000000..aae660aed461 --- /dev/null +++ b/Packs/Zendesk/ReleaseNotes/2_1_0.md @@ -0,0 +1,19 @@ + +#### Integrations + +##### Zendesk v2 + +- Added new commands: + - ***zendesk-group-user-list*** + - ***zendesk-group-list*** +- Added the integration parameter *Get incident attachments* to allow getting the ticket attachment while fetching incidents. +- Added the *comment* argument to the ***zendesk-ticket-create*** command. +- Added the argument *html_comment* to the following commands: + - ***zendesk-ticket-create*** + - ***zendesk-ticket-update*** +- Updated the Docker image to: *demisto/python3:3.10.12.63474*. + + +#### Mappers +##### Zendesk - Incoming Mapper +Added mapping for the *group_id* field to the *Assignment Group* incident field. \ No newline at end of file diff --git a/Packs/Zendesk/TestPlaybooks/Zendesk_V2_TEST.yml b/Packs/Zendesk/TestPlaybooks/Zendesk_V2_TEST.yml index eb7d28d2b6a4..3fa1d7fd7ca1 100644 --- a/Packs/Zendesk/TestPlaybooks/Zendesk_V2_TEST.yml +++ b/Packs/Zendesk/TestPlaybooks/Zendesk_V2_TEST.yml @@ -5,10 +5,10 @@ starttaskid: "0" tasks: "0": id: "0" - taskid: eff64d56-8c8a-4f04-8b55-be6aa7aceeb9 + taskid: 1a58b3e2-39c3-4309-88e0-37b7a9e06326 type: start task: - id: eff64d56-8c8a-4f04-8b55-be6aa7aceeb9 + id: 1a58b3e2-39c3-4309-88e0-37b7a9e06326 version: -1 name: "" iscommand: false @@ -35,10 +35,10 @@ tasks: isautoswitchedtoquietmode: false "1": id: "1" - taskid: ce2d645f-9d5e-4c81-8929-440fd5f53287 + taskid: 78a80279-336d-4401-8c99-3465dba7e409 type: title task: - id: ce2d645f-9d5e-4c81-8929-440fd5f53287 + id: 78a80279-336d-4401-8c99-3465dba7e409 version: -1 name: Users tasks type: title @@ -66,10 +66,10 @@ tasks: isautoswitchedtoquietmode: false "2": id: "2" - taskid: af405b2f-8066-488f-8136-49309574a913 + taskid: a46347a2-036d-4f0a-8838-3b9b5c5374b9 type: regular task: - id: af405b2f-8066-488f-8136-49309574a913 + id: a46347a2-036d-4f0a-8838-3b9b5c5374b9 version: -1 name: get users (init - might fail) script: 'Zendesk v2|||zendesk-user-list' @@ -98,10 +98,10 @@ tasks: isautoswitchedtoquietmode: false "3": id: "3" - taskid: 08ee78d7-1e25-4ab0-8ae6-452144b14103 + taskid: 6a204bf3-2b1c-4f03-808b-fd9ccaac1c1f type: regular task: - id: 08ee78d7-1e25-4ab0-8ae6-452144b14103 + id: 6a204bf3-2b1c-4f03-808b-fd9ccaac1c1f version: -1 name: delete context description: |- @@ -138,10 +138,10 @@ tasks: isautoswitchedtoquietmode: false "4": id: "4" - taskid: e133df15-4c9b-4794-8055-dc1a277b15e1 + taskid: 47f8adab-1624-43d6-8d88-ca198c82ba0f type: regular task: - id: e133df15-4c9b-4794-8055-dc1a277b15e1 + id: 47f8adab-1624-43d6-8d88-ca198c82ba0f version: -1 name: delete user (init - might fail) script: 'Zendesk v2|||zendesk-user-delete' @@ -194,10 +194,10 @@ tasks: isautoswitchedtoquietmode: false "5": id: "5" - taskid: 17962174-5da9-4ed9-8115-b5ff26318763 + taskid: 054ea3d3-9243-494f-84e0-5efa8e7d2bb5 type: regular task: - id: 17962174-5da9-4ed9-8115-b5ff26318763 + id: 054ea3d3-9243-494f-84e0-5efa8e7d2bb5 version: -1 name: create user script: 'Zendesk v2|||zendesk-user-create' @@ -240,10 +240,10 @@ tasks: isautoswitchedtoquietmode: false "6": id: "6" - taskid: 53bb421c-dab7-45ea-8c3d-51019f319f60 + taskid: 3ac6fdd8-cc5f-4930-8225-d4994fce5d4c type: regular task: - id: 53bb421c-dab7-45ea-8c3d-51019f319f60 + id: 3ac6fdd8-cc5f-4930-8225-d4994fce5d4c version: -1 name: delete users content description: |- @@ -280,10 +280,10 @@ tasks: isautoswitchedtoquietmode: false "7": id: "7" - taskid: f93beeea-d84a-4590-84f5-a8a2c9288929 + taskid: 3e789545-b2e2-431b-840a-0dd5903b5c6f type: regular task: - id: f93beeea-d84a-4590-84f5-a8a2c9288929 + id: 3e789545-b2e2-431b-840a-0dd5903b5c6f version: -1 name: list users script: 'Zendesk v2|||zendesk-user-list' @@ -311,10 +311,10 @@ tasks: isautoswitchedtoquietmode: false "8": id: "8" - taskid: a13c60ac-27d6-4325-8b78-8adb1f980103 + taskid: 6f799e61-420f-4348-8bcd-c69d27a4d3af type: condition task: - id: a13c60ac-27d6-4325-8b78-8adb1f980103 + id: 6f799e61-420f-4348-8bcd-c69d27a4d3af version: -1 name: check that the user exists type: condition @@ -371,10 +371,10 @@ tasks: isautoswitchedtoquietmode: false "9": id: "9" - taskid: c43849b5-a12b-4b0b-8c77-4578fc9e6443 + taskid: a9028e8b-e5ac-41a3-862f-b8c921f73fd0 type: regular task: - id: c43849b5-a12b-4b0b-8c77-4578fc9e6443 + id: a9028e8b-e5ac-41a3-862f-b8c921f73fd0 version: -1 name: update user script: 'Zendesk v2|||zendesk-user-update' @@ -428,10 +428,10 @@ tasks: isautoswitchedtoquietmode: false "10": id: "10" - taskid: e28d747b-7f32-46e5-83f0-2dd4d918035e + taskid: a6111c8a-405b-446c-80d1-01ea3d43e570 type: regular task: - id: e28d747b-7f32-46e5-83f0-2dd4d918035e + id: a6111c8a-405b-446c-80d1-01ea3d43e570 version: -1 name: delete users content description: |- @@ -456,7 +456,7 @@ tasks: { "position": { "x": 470, - "y": 2790 + "y": 2965 } } note: false @@ -468,10 +468,10 @@ tasks: isautoswitchedtoquietmode: false "11": id: "11" - taskid: d5f7b7d5-c51b-427a-8edc-28b9d97827d0 + taskid: 02f88c4f-5d4a-48fc-84f6-72ece1bbf421 type: condition task: - id: d5f7b7d5-c51b-427a-8edc-28b9d97827d0 + id: 02f88c4f-5d4a-48fc-84f6-72ece1bbf421 version: -1 name: check user update type: condition @@ -481,7 +481,7 @@ tasks: '#default#': - "16" "yes": - - "25" + - "42" separatecontext: false conditions: - label: "yes" @@ -533,10 +533,10 @@ tasks: isautoswitchedtoquietmode: false "12": id: "12" - taskid: ff05dc3d-8aac-4b5f-8ee1-7119dbfc74bf + taskid: f1776775-1df6-4854-8299-1eb9460a09cb type: regular task: - id: ff05dc3d-8aac-4b5f-8ee1-7119dbfc74bf + id: f1776775-1df6-4854-8299-1eb9460a09cb version: -1 name: delete user script: 'Zendesk v2|||zendesk-user-delete' @@ -576,7 +576,7 @@ tasks: { "position": { "x": 470, - "y": 2615 + "y": 2790 } } note: false @@ -588,10 +588,10 @@ tasks: isautoswitchedtoquietmode: false "13": id: "13" - taskid: fce06ff8-fcf8-408a-8b72-2c895fc3974f + taskid: 634b0a2f-47bc-41e4-8f9e-150c7c612a7a type: regular task: - id: fce06ff8-fcf8-408a-8b72-2c895fc3974f + id: 634b0a2f-47bc-41e4-8f9e-150c7c612a7a version: -1 name: get users script: 'Zendesk v2|||zendesk-user-list' @@ -607,7 +607,7 @@ tasks: { "position": { "x": 470, - "y": 2965 + "y": 3140 } } note: false @@ -619,10 +619,10 @@ tasks: isautoswitchedtoquietmode: false "14": id: "14" - taskid: 419b5928-a2fc-4975-853e-2e62b66cba59 + taskid: c5b67662-bece-4308-84d1-16fa83ed8ce7 type: condition task: - id: 419b5928-a2fc-4975-853e-2e62b66cba59 + id: c5b67662-bece-4308-84d1-16fa83ed8ce7 version: -1 name: 'check that user does not exist ' type: condition @@ -667,7 +667,7 @@ tasks: { "position": { "x": 470, - "y": 3140 + "y": 3315 } } note: false @@ -679,10 +679,10 @@ tasks: isautoswitchedtoquietmode: false "15": id: "15" - taskid: 1faa4883-dd2e-4674-87d7-7db996024bfa + taskid: 2e70ee63-6349-4b46-8c85-52b92b00f793 type: title task: - id: 1faa4883-dd2e-4674-87d7-7db996024bfa + id: 2e70ee63-6349-4b46-8c85-52b92b00f793 version: -1 name: users test done type: title @@ -698,7 +698,7 @@ tasks: { "position": { "x": 582.5, - "y": 3315 + "y": 3490 } } note: false @@ -710,10 +710,10 @@ tasks: isautoswitchedtoquietmode: false "16": id: "16" - taskid: 02d3a099-c70d-46ab-895b-647be93e934f + taskid: d48a9f95-3aaa-421c-8f3d-4d9fb483632a type: regular task: - id: 02d3a099-c70d-46ab-895b-647be93e934f + id: d48a9f95-3aaa-421c-8f3d-4d9fb483632a version: -1 name: validation error description: Prints an error entry with a given message @@ -730,7 +730,7 @@ tasks: { "position": { "x": 50, - "y": 6870 + "y": 7715 } } note: false @@ -742,10 +742,10 @@ tasks: isautoswitchedtoquietmode: false "17": id: "17" - taskid: 4fe2aaee-0c89-463f-8833-e489937878a0 + taskid: e9a334b7-6468-469c-8939-f497e26ec59b type: title task: - id: 4fe2aaee-0c89-463f-8833-e489937878a0 + id: e9a334b7-6468-469c-8939-f497e26ec59b version: -1 name: Organization tasks type: title @@ -761,7 +761,7 @@ tasks: { "position": { "x": 582.5, - "y": 3635 + "y": 3810 } } note: false @@ -773,10 +773,10 @@ tasks: isautoswitchedtoquietmode: false "18": id: "18" - taskid: 99ff2555-6dca-4eeb-8dcd-4aad8d167b77 + taskid: f3fca523-a00a-47e1-8163-965f54697fb0 type: regular task: - id: 99ff2555-6dca-4eeb-8dcd-4aad8d167b77 + id: f3fca523-a00a-47e1-8163-965f54697fb0 version: -1 name: list organizations script: 'Zendesk v2|||zendesk-organization-list' @@ -792,7 +792,7 @@ tasks: { "position": { "x": 582.5, - "y": 3780 + "y": 3955 } } note: false @@ -804,10 +804,10 @@ tasks: isautoswitchedtoquietmode: false "19": id: "19" - taskid: 7083346e-c9ad-4876-820e-f5f6505aaf60 + taskid: 6c4251af-6312-4538-8dc2-2d2f1463c5f1 type: condition task: - id: 7083346e-c9ad-4876-820e-f5f6505aaf60 + id: 6c4251af-6312-4538-8dc2-2d2f1463c5f1 version: -1 name: check organizations type: condition @@ -832,7 +832,7 @@ tasks: { "position": { "x": 582.5, - "y": 3955 + "y": 4130 } } note: false @@ -844,10 +844,10 @@ tasks: isautoswitchedtoquietmode: false "20": id: "20" - taskid: a978f2d9-7a78-4d14-854a-5a23cfb645f2 + taskid: d72b255a-47ae-41a3-8b37-cdf72239d5ad type: title task: - id: a978f2d9-7a78-4d14-854a-5a23cfb645f2 + id: d72b255a-47ae-41a3-8b37-cdf72239d5ad version: -1 name: Organizations test done type: title @@ -863,7 +863,7 @@ tasks: { "position": { "x": 695, - "y": 4130 + "y": 4305 } } note: false @@ -875,10 +875,10 @@ tasks: isautoswitchedtoquietmode: false "21": id: "21" - taskid: 22f60cca-be39-4e2a-86cf-063b16725a1e + taskid: 87cdb0fc-15ba-4387-872e-15ae7841e96d type: title task: - id: 22f60cca-be39-4e2a-86cf-063b16725a1e + id: 87cdb0fc-15ba-4387-872e-15ae7841e96d version: -1 name: Done type: title @@ -891,7 +891,7 @@ tasks: { "position": { "x": 705, - "y": 6885 + "y": 7730 } } note: false @@ -903,10 +903,10 @@ tasks: isautoswitchedtoquietmode: false "22": id: "22" - taskid: d06c6dab-8549-4b4e-8920-a0c3e86c97f3 + taskid: 7beb3bc7-3492-4eb8-814c-c9d124b45e7d type: title task: - id: d06c6dab-8549-4b4e-8920-a0c3e86c97f3 + id: 7beb3bc7-3492-4eb8-814c-c9d124b45e7d version: -1 name: Ticket tests type: title @@ -922,7 +922,7 @@ tasks: { "position": { "x": 695, - "y": 4450 + "y": 5295 } } note: false @@ -934,10 +934,10 @@ tasks: isautoswitchedtoquietmode: false "23": id: "23" - taskid: b6b7ad55-5302-4363-859c-b9ac1afd41e8 + taskid: 3d47a2d7-0aa8-49e0-800d-809900ebc4c6 type: regular task: - id: b6b7ad55-5302-4363-859c-b9ac1afd41e8 + id: 3d47a2d7-0aa8-49e0-800d-809900ebc4c6 version: -1 name: create ticket script: 'Zendesk v2|||zendesk-ticket-create' @@ -966,7 +966,7 @@ tasks: { "position": { "x": 695, - "y": 4770 + "y": 5615 } } note: false @@ -978,10 +978,10 @@ tasks: isautoswitchedtoquietmode: false "24": id: "24" - taskid: 011293ae-aaf8-4772-8f83-9b24d3b74821 + taskid: 5a50bc4d-c4d2-4043-869f-7312a591bc5c type: regular task: - id: 011293ae-aaf8-4772-8f83-9b24d3b74821 + id: 5a50bc4d-c4d2-4043-869f-7312a591bc5c version: -1 name: get user script: 'Zendesk v2|||zendesk-user-list' @@ -1006,7 +1006,7 @@ tasks: { "position": { "x": 695, - "y": 4595 + "y": 5440 } } note: false @@ -1018,10 +1018,10 @@ tasks: isautoswitchedtoquietmode: false "25": id: "25" - taskid: 29d973de-56dd-4b4e-83ac-8ff293fbdbba + taskid: c4302dc7-2bdf-4311-89c6-cb210a34940f type: regular task: - id: 29d973de-56dd-4b4e-83ac-8ff293fbdbba + id: c4302dc7-2bdf-4311-89c6-cb210a34940f version: -1 name: delete users content description: |- @@ -1046,7 +1046,7 @@ tasks: { "position": { "x": 470, - "y": 2265 + "y": 2440 } } note: false @@ -1058,10 +1058,10 @@ tasks: isautoswitchedtoquietmode: false "26": id: "26" - taskid: 858d776f-00e2-4549-853b-495126bfe2e5 + taskid: cba83f35-0dff-4e79-8091-795f8052f5a4 type: regular task: - id: 858d776f-00e2-4549-853b-495126bfe2e5 + id: cba83f35-0dff-4e79-8091-795f8052f5a4 version: -1 name: get users script: 'Zendesk v2|||zendesk-user-list' @@ -1077,7 +1077,7 @@ tasks: { "position": { "x": 470, - "y": 2440 + "y": 2615 } } note: false @@ -1089,10 +1089,10 @@ tasks: isautoswitchedtoquietmode: false "27": id: "27" - taskid: e092f74f-7d43-47f5-83a2-2b57665ca482 + taskid: 55a3f4c8-ff43-4b33-8b80-9cc70031b8be type: regular task: - id: e092f74f-7d43-47f5-83a2-2b57665ca482 + id: 55a3f4c8-ff43-4b33-8b80-9cc70031b8be version: -1 name: ' update ticket' script: 'Zendesk v2|||zendesk-ticket-update' @@ -1113,7 +1113,7 @@ tasks: { "position": { "x": 695, - "y": 4945 + "y": 5790 } } note: false @@ -1125,10 +1125,10 @@ tasks: isautoswitchedtoquietmode: false "28": id: "28" - taskid: 1d0b86cc-5e69-4314-83b5-0b6577441543 + taskid: 356520fe-2c6e-4d24-86cc-2d03e9cdb667 type: regular task: - id: 1d0b86cc-5e69-4314-83b5-0b6577441543 + id: 356520fe-2c6e-4d24-86cc-2d03e9cdb667 version: -1 name: list comments script: 'Zendesk v2|||zendesk-ticket-comment-list' @@ -1147,7 +1147,7 @@ tasks: { "position": { "x": 695, - "y": 5470 + "y": 6315 } } note: false @@ -1159,10 +1159,10 @@ tasks: isautoswitchedtoquietmode: false "29": id: "29" - taskid: 97de5712-fa86-4a93-8509-3d4646e1a886 + taskid: becdbbb8-09da-4ad0-88d4-cbffdbd86a86 type: regular task: - id: 97de5712-fa86-4a93-8509-3d4646e1a886 + id: becdbbb8-09da-4ad0-88d4-cbffdbd86a86 version: -1 name: create a file to upload description: | @@ -1187,7 +1187,7 @@ tasks: { "position": { "x": 695, - "y": 5120 + "y": 5965 } } note: false @@ -1199,10 +1199,10 @@ tasks: isautoswitchedtoquietmode: false "30": id: "30" - taskid: 42c6bfc1-1f8f-49de-8013-1b7a10dd5732 + taskid: e682ed0e-476a-4969-8607-58586a4e4daf type: regular task: - id: 42c6bfc1-1f8f-49de-8013-1b7a10dd5732 + id: e682ed0e-476a-4969-8607-58586a4e4daf version: -1 name: upload attachment script: 'Zendesk v2|||zendesk-ticket-attachment-add' @@ -1225,7 +1225,7 @@ tasks: { "position": { "x": 695, - "y": 5295 + "y": 6140 } } note: false @@ -1237,10 +1237,10 @@ tasks: isautoswitchedtoquietmode: false "31": id: "31" - taskid: 10e203d0-04c8-4a72-88e2-30571615f50d + taskid: ab243edb-793a-40bd-847f-c768c5fb65ef type: regular task: - id: 10e203d0-04c8-4a72-88e2-30571615f50d + id: ab243edb-793a-40bd-847f-c768c5fb65ef version: -1 name: get attacment script: 'Zendesk v2|||zendesk-attachment-get' @@ -1267,7 +1267,7 @@ tasks: { "position": { "x": 695, - "y": 5645 + "y": 6490 } } note: false @@ -1279,10 +1279,10 @@ tasks: isautoswitchedtoquietmode: false "32": id: "32" - taskid: 589b00bd-e3a0-4f39-8165-ae0bea120941 + taskid: b81918db-94c9-48a2-872c-3169e520b8f4 type: regular task: - id: 589b00bd-e3a0-4f39-8165-ae0bea120941 + id: b81918db-94c9-48a2-872c-3169e520b8f4 version: -1 name: read file description: Load the contents of a file into context. @@ -1304,7 +1304,7 @@ tasks: { "position": { "x": 695, - "y": 5820 + "y": 6665 } } note: false @@ -1316,10 +1316,10 @@ tasks: isautoswitchedtoquietmode: false "33": id: "33" - taskid: 564f579a-4ab4-4452-805d-f199868ecc3f + taskid: 94fd3436-82c2-453c-80c6-d2ebe4b35e18 type: condition task: - id: 564f579a-4ab4-4452-805d-f199868ecc3f + id: 94fd3436-82c2-453c-80c6-d2ebe4b35e18 version: -1 name: check file content type: condition @@ -1348,7 +1348,7 @@ tasks: { "position": { "x": 695, - "y": 6695 + "y": 7540 } } note: false @@ -1360,10 +1360,10 @@ tasks: isautoswitchedtoquietmode: false "34": id: "34" - taskid: 65adce83-0f82-43b6-8c95-89dca04ca86f + taskid: 6cd71a41-ea4b-4252-803b-8f544758e98b type: regular task: - id: 65adce83-0f82-43b6-8c95-89dca04ca86f + id: 6cd71a41-ea4b-4252-803b-8f544758e98b version: -1 name: read info file description: Load the contents of a file into context. @@ -1385,7 +1385,7 @@ tasks: { "position": { "x": 695, - "y": 6345 + "y": 7190 } } note: false @@ -1397,10 +1397,10 @@ tasks: isautoswitchedtoquietmode: false "35": id: "35" - taskid: bb9d9a02-e580-4ded-8f44-c33061a41403 + taskid: 357ca8e7-fe53-42c1-8a95-b442394031cd type: regular task: - id: bb9d9a02-e580-4ded-8f44-c33061a41403 + id: 357ca8e7-fe53-42c1-8a95-b442394031cd version: -1 name: set file data description: Set a value in context under the key you entered. @@ -1422,7 +1422,7 @@ tasks: { "position": { "x": 695, - "y": 5995 + "y": 6840 } } note: false @@ -1434,10 +1434,10 @@ tasks: isautoswitchedtoquietmode: false "36": id: "36" - taskid: 994cbaab-d5ae-4c11-85fc-6af955d82af5 + taskid: 15e2d39d-6456-4362-88d0-b494bc6b712e type: regular task: - id: 994cbaab-d5ae-4c11-85fc-6af955d82af5 + id: 15e2d39d-6456-4362-88d0-b494bc6b712e version: -1 name: set file data description: Set a value in context under the key you entered. @@ -1459,7 +1459,7 @@ tasks: { "position": { "x": 695, - "y": 6520 + "y": 7365 } } note: false @@ -1471,10 +1471,10 @@ tasks: isautoswitchedtoquietmode: false "37": id: "37" - taskid: 800a417b-ea81-4520-8fbe-f4add52200bb + taskid: bab8eb32-892f-4af2-879d-a1750f065ac8 type: regular task: - id: 800a417b-ea81-4520-8fbe-f4add52200bb + id: bab8eb32-892f-4af2-879d-a1750f065ac8 version: -1 name: delete old file content description: |- @@ -1499,7 +1499,7 @@ tasks: { "position": { "x": 695, - "y": 6170 + "y": 7015 } } note: false @@ -1511,10 +1511,10 @@ tasks: isautoswitchedtoquietmode: false "38": id: "38" - taskid: bfa25e3d-6d39-4675-8e11-ba075be0e122 + taskid: 882e9c30-766f-42c3-8cdb-6e5322846d76 type: regular task: - id: bfa25e3d-6d39-4675-8e11-ba075be0e122 + id: 882e9c30-766f-42c3-8cdb-6e5322846d76 version: -1 name: list tickets with this user (init - might fail) description: List Zendesk tickets. @@ -1567,12 +1567,13 @@ tasks: quietmode: 0 isoversize: false isautoswitchedtoquietmode: false + continueonerrortype: "" "39": id: "39" - taskid: 97215936-ecb1-4586-8e8b-63a1180340a7 + taskid: 75cae003-5bf0-4b10-8a99-0e07b2603695 type: regular task: - id: 97215936-ecb1-4586-8e8b-63a1180340a7 + id: 75cae003-5bf0-4b10-8a99-0e07b2603695 version: -1 name: close tickets (init - might fail). description: Updates a Zendesk ticket. @@ -1604,12 +1605,13 @@ tasks: quietmode: 0 isoversize: false isautoswitchedtoquietmode: false + continueonerrortype: "" "40": id: "40" - taskid: defd7008-9f45-4444-8c1a-dcef9df73094 + taskid: d27415d5-0b82-4265-8620-a78262ab541b type: regular task: - id: defd7008-9f45-4444-8c1a-dcef9df73094 + id: d27415d5-0b82-4265-8620-a78262ab541b version: -1 name: delete context description: |- @@ -1624,7 +1626,7 @@ tasks: brand: "" nexttasks: '#none#': - - "22" + - "43" scriptarguments: all: simple: "yes" @@ -1634,7 +1636,7 @@ tasks: { "position": { "x": 695, - "y": 4275 + "y": 4450 } } note: false @@ -1646,10 +1648,10 @@ tasks: isautoswitchedtoquietmode: false "41": id: "41" - taskid: c6b496f8-6cdb-4ef4-8e59-e13e5d221eb3 + taskid: ae40dae8-9541-400c-8c69-40116cef7c2e type: regular task: - id: c6b496f8-6cdb-4ef4-8e59-e13e5d221eb3 + id: ae40dae8-9541-400c-8c69-40116cef7c2e version: -1 name: delete context description: |- @@ -1674,7 +1676,183 @@ tasks: { "position": { "x": 582.5, - "y": 3460 + "y": 3635 + } + } + note: false + timertriggers: [] + ignoreworker: false + skipunavailable: false + quietmode: 0 + isoversize: false + isautoswitchedtoquietmode: false + "42": + id: "42" + taskid: 555736d4-8bba-4cdc-8da8-edb6c04194ce + type: regular + task: + id: 555736d4-8bba-4cdc-8da8-edb6c04194ce + version: -1 + name: Get the user's group + description: "Get group's users. \nAllowed for: Admins, Agents and Light Agents." + script: '|||zendesk-group-user-list' + type: regular + iscommand: true + brand: "" + nexttasks: + '#none#': + - "25" + scriptarguments: + group_id: + simple: ${Zendesk.User.default_group_id} + separatecontext: false + continueonerrortype: "" + view: |- + { + "position": { + "x": 470, + "y": 2265 + } + } + note: false + timertriggers: [] + ignoreworker: false + skipunavailable: false + quietmode: 0 + isoversize: false + isautoswitchedtoquietmode: false + "43": + id: "43" + taskid: 137abb2d-84d3-4f47-8bbb-284d8464c86b + type: title + task: + id: 137abb2d-84d3-4f47-8bbb-284d8464c86b + version: -1 + name: Group Test + type: title + iscommand: false + brand: "" + description: '' + nexttasks: + '#none#': + - "44" + separatecontext: false + continueonerrortype: "" + view: |- + { + "position": { + "x": 695, + "y": 4625 + } + } + note: false + timertriggers: [] + ignoreworker: false + skipunavailable: false + quietmode: 0 + isoversize: false + isautoswitchedtoquietmode: false + "44": + id: "44" + taskid: bc54dd3d-c27e-476b-887b-0d3c8840985a + type: regular + task: + id: bc54dd3d-c27e-476b-887b-0d3c8840985a + version: -1 + name: Get Zendesk groups + description: "Get Zendesk groups. \nAllowed for: Admins, Agents." + script: '|||zendesk-group-list' + type: regular + iscommand: true + brand: "" + nexttasks: + '#none#': + - "45" + separatecontext: false + continueonerrortype: "" + view: |- + { + "position": { + "x": 695, + "y": 4770 + } + } + note: false + timertriggers: [] + ignoreworker: false + skipunavailable: false + quietmode: 0 + isoversize: false + isautoswitchedtoquietmode: false + "45": + id: "45" + taskid: 8bb2112d-114a-4124-86a1-73c159c92eeb + type: condition + task: + id: 8bb2112d-114a-4124-86a1-73c159c92eeb + version: -1 + name: Check Groups + type: condition + iscommand: false + brand: "" + nexttasks: + "yes": + - "46" + separatecontext: false + conditions: + - label: "yes" + condition: + - - operator: isNotEmpty + left: + value: + simple: Zendesk.Group.id + iscontext: true + continueonerrortype: "" + view: |- + { + "position": { + "x": 695, + "y": 4945 + } + } + note: false + timertriggers: [] + ignoreworker: false + skipunavailable: false + quietmode: 0 + isoversize: false + isautoswitchedtoquietmode: false + "46": + id: "46" + taskid: 3027a175-8d79-461f-8981-2948507d9e2e + type: regular + task: + id: 3027a175-8d79-461f-8981-2948507d9e2e + version: -1 + name: Delete context - done group test + description: |- + Delete field from context. + + This automation runs using the default Limited User role, unless you explicitly change the permissions. + For more information, see the section about permissions here: + https://docs-cortex.paloaltonetworks.com/r/Cortex-XSOAR/6.10/Cortex-XSOAR-Administrator-Guide/Automations + scriptName: DeleteContext + type: regular + iscommand: false + brand: "" + nexttasks: + '#none#': + - "22" + scriptarguments: + all: + simple: "yes" + separatecontext: false + continueonerrortype: "" + view: |- + { + "position": { + "x": 695, + "y": 5120 } } note: false @@ -1689,7 +1867,7 @@ view: |- "linkLabelsPosition": {}, "paper": { "dimensions": { - "height": 6915, + "height": 7760, "width": 1035, "x": 50, "y": 50 diff --git a/Packs/Zendesk/pack_metadata.json b/Packs/Zendesk/pack_metadata.json index 213f03d36050..1a1d6045658d 100644 --- a/Packs/Zendesk/pack_metadata.json +++ b/Packs/Zendesk/pack_metadata.json @@ -2,7 +2,7 @@ "name": "Zendesk", "description": "IT service management", "support": "xsoar", - "currentVersion": "2.0.7", + "currentVersion": "2.1.0", "author": "Cortex XSOAR", "url": "https://www.paloaltonetworks.com/cortex", "email": "",