PATHelpdeskAdvanced
Improve the effectiveness of your service provision and resources, and the quality of your IT department.
Utilities · PAT Helpdesk Advanced
Details
| ID | PATHelpdeskAdvanced |
|---|---|
| Provider | PATServicesLTD |
| Category | Utilities |
| From Version | 6.9.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
Improve the effectiveness of your service provision and resources, and the quality of your IT department.
This integration was integrated and tested with version 11.2.3 of PATHelpdeskAdvanced.
Configure PAT HelpdeskAdvanced in Cortex
| Parameter | Required |
|---|---|
| PAT Helpdesk URL | True |
| Username | True |
| Password | True |
| Trust any certificate (not secure) | False |
| Use system proxy settings | False |
Commands
You can execute these commands from the 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.
hda-create-ticket
Create a new ticket.
Base Command
hda-create-ticket
Input
| Argument Name | Description | Required |
|---|---|---|
| object_type_id | The object type ID for the ticket. | Required |
| ticket_status_id | The status ID for the ticket. Use hda-list-ticket-statuses to get a list of status IDs. | Required |
| ticket_priority_id | The priority ID for the ticket. Use hda-list-ticket-priorities to get a list of priority IDs. | Required |
| object_description | The description of the object. | Optional |
| ticket_classification_id | The classification ID for the ticket. | Optional |
| ticket_type_id | The type ID for the ticket. | Optional |
| contact_id | The contact ID for the ticket. | Optional |
| subject | The subject for the ticket. | Optional |
| problem | The problem description for the ticket. | Optional |
| site | The site for the ticket. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| HelpdeskAdvanced.Ticket.AccountID | string | The account ID of the ticket. |
| HelpdeskAdvanced.Ticket.AssetCategoryID | string | The asset category ID of the ticket. |
| HelpdeskAdvanced.Ticket.AssetID | string | The asset ID of the ticket. |
| HelpdeskAdvanced.Ticket.AssignedUserGroupID | string | The assigned user group ID of the ticket. |
| HelpdeskAdvanced.Ticket.AssignedUserID | string | The assigned user ID of the ticket. |
| HelpdeskAdvanced.Ticket.AssignedUserOrGroupID | string | The assigned user or group ID of the ticket. |
| HelpdeskAdvanced.Ticket.BilledTokens | string | The billed tokens of the ticket. |
| HelpdeskAdvanced.Ticket.BusinessFunctionID | string | The business function ID of the ticket. |
| HelpdeskAdvanced.Ticket.C12 | string | The C12 field of the ticket. |
| HelpdeskAdvanced.Ticket.C13 | string | The C13 field of the ticket. |
| HelpdeskAdvanced.Ticket.C134C | string | The C134C field of the ticket. |
| HelpdeskAdvanced.Ticket.C14 | string | The C14 field of the ticket. |
| HelpdeskAdvanced.Ticket.C15 | string | The C15 field of the ticket. |
| HelpdeskAdvanced.Ticket.CalendarID | string | The calendar ID of the ticket. |
| HelpdeskAdvanced.Ticket.ClosedByUserID | string | The ID of the user who closed the ticket. |
| HelpdeskAdvanced.Ticket.ClosureDate | string | The closure date of the ticket. |
| HelpdeskAdvanced.Ticket.ContactID | string | The contact ID of the ticket. |
| HelpdeskAdvanced.Ticket.CostCenterID | string | The cost center ID of the ticket. |
| HelpdeskAdvanced.Ticket.CustomerContractID | string | The customer contract ID of the ticket. |
| HelpdeskAdvanced.Ticket.Date | date | The date of the ticket. |
| HelpdeskAdvanced.Ticket.DefaultSolutionID | string | The default solution ID of the ticket. |
| HelpdeskAdvanced.Ticket.EstimatedTaskDuration | string | The estimated task duration of the ticket. |
| HelpdeskAdvanced.Ticket.EstimatedTaskStartDate | string | The estimated task start date of the ticket. |
| HelpdeskAdvanced.Ticket.ExpirationDate | date | The expiration date of the ticket. |
| HelpdeskAdvanced.Ticket.FirstUpdateUserID | string | The ID of the user who first updated the ticket. |
| HelpdeskAdvanced.Ticket.FullText | string | The full text of the ticket. |
| HelpdeskAdvanced.Ticket.ID | string | The ID of the ticket. |
| HelpdeskAdvanced.Ticket.ImpactID | string | The impact ID of the ticket. |
| HelpdeskAdvanced.Ticket.IsNew | boolean | Whether the ticket is new or not. |
| HelpdeskAdvanced.Ticket.KnownIssue | string | The known issue field of the ticket. |
| HelpdeskAdvanced.Ticket.LanguageID | string | The language ID of the ticket. |
| HelpdeskAdvanced.Ticket.LastExpirationDate | date | The last expiration date of the ticket. |
| HelpdeskAdvanced.Ticket.LastExpirationID | string | The last expiration ID of the ticket. |
| HelpdeskAdvanced.Ticket.LocationID | string | The location ID of the ticket. |
| HelpdeskAdvanced.Ticket.MailBoxID | string | The mailbox ID of the ticket. |
| HelpdeskAdvanced.Ticket.NextExpirationDate | string | The next expiration date of the ticket. |
| HelpdeskAdvanced.Ticket.NextExpirationID | string | The next expiration ID of the ticket. |
| HelpdeskAdvanced.Ticket.ObjectDescription | string | The object description of the ticket. |
| HelpdeskAdvanced.Ticket.ObjectEntity | string | The object entity of the ticket. |
| HelpdeskAdvanced.Ticket.ObjectTypeID | string | The object type ID of the ticket. |
| HelpdeskAdvanced.Ticket.OwnerUserGroupID | string | The owner user group ID of the ticket. |
| HelpdeskAdvanced.Ticket.OwnerUserID | string | The owner user ID of the ticket. |
| HelpdeskAdvanced.Ticket.ParentRecurringTicketID | string | The parent recurring ticket ID of the ticket. |
| HelpdeskAdvanced.Ticket.ParentTicketID | string | The parent ticket ID of the ticket. |
| HelpdeskAdvanced.Ticket.Problem | string | The problem field of the ticket. |
| HelpdeskAdvanced.Ticket.ProblemHTML | string | The problem field in HTML format of the ticket. |
| HelpdeskAdvanced.Ticket.RemoteID | string | The remote ID of the ticket. |
| HelpdeskAdvanced.Ticket.Score | string | The score of the ticket. |
| HelpdeskAdvanced.Ticket.ServiceID | string | The service ID of the ticket. |
| HelpdeskAdvanced.Ticket.Site | string | The site of the ticket. |
| HelpdeskAdvanced.Ticket.SiteUnRead | string | Whether the ticket is unread for the site. |
| HelpdeskAdvanced.Ticket.Solicits | string | Whether the ticket solicits feedback. |
| HelpdeskAdvanced.Ticket.SolutionHTML | string | The solution in HTML format of the ticket. |
| HelpdeskAdvanced.Ticket.SourceMailBoxID | string | The source mailbox ID of the ticket. |
| HelpdeskAdvanced.Ticket.Subject | string | The subject of the ticket. |
| HelpdeskAdvanced.Ticket.SupplierID | string | The supplier ID of the ticket. |
| HelpdeskAdvanced.Ticket.Tag | string | The tag of the ticket. |
| HelpdeskAdvanced.Ticket.TaskEffort | string | The task effort of the ticket. |
| HelpdeskAdvanced.Ticket.TemplateTicketID | string | The template ticket ID of the ticket. |
| HelpdeskAdvanced.Ticket.TicketClassificationID | string | The ticket classification ID of the ticket. |
| HelpdeskAdvanced.Ticket.TicketCode | string | The ticket code of the ticket. |
| HelpdeskAdvanced.Ticket.TicketPriorityID | string | The ticket priority ID of the ticket. |
| HelpdeskAdvanced.Ticket.TicketSolutionID | string | The ticket solution ID of the ticket. |
| HelpdeskAdvanced.Ticket.TicketSourceID | string | The ticket source ID of the ticket. |
| HelpdeskAdvanced.Ticket.TicketStatusID | string | The ticket status ID of the ticket. |
| HelpdeskAdvanced.Ticket.TicketTypeID | string | The ticket type ID of the ticket. |
| HelpdeskAdvanced.Ticket.UnRead | string | Whether the ticket is unread. |
| HelpdeskAdvanced.Ticket.UrgencyID | string | The urgency ID of the ticket. |
| HelpdeskAdvanced.Ticket.UserID | string | The user ID of the ticket. |
Command example
!hda-create-ticket object_type_id=T2 ticket_priority_id=P4 ticket_status_id=S3
Context Example
{
"HelpdeskAdvanced": {
"Ticket": {
"data": {
"AccountID": "",
"AssetCategoryID": "",
"AssetID": "",
"AssignedUserGroupID": "",
"AssignedUserID": "S00000C",
"AssignedUserOrGroupID": "S00000C",
"BilledTokens": 0,
"BusinessFunctionID": "",
"C12": null,
"C13": null,
"C134C": null,
"C14": null,
"C15": null,
"CalendarID": "",
"ClosedByUserID": "",
"ClosureDate": null,
"ContactID": "",
"CostCenterID": "",
"CustomerContractID": "",
"Date": "2023-10-04T07:45:09Z",
"DefaultSolutionID": "",
"EstimatedTaskDuration": 0,
"EstimatedTaskStartDate": null,
"ExpirationDate": "2023-10-04T15:45:00Z",
"FirstUpdateUserID": "S00000C",
"FullText": "",
"ID": "12345678C",
"ImpactID": "",
"IsNew": false,
"KnownIssue": false,
"LanguageID": "0",
"LastExpirationDate": "2998-12-31T23:00:00Z",
"LastExpirationID": "",
"LocationID": "",
"MailBoxID": "DEF_MBOX",
"NextExpirationDate": "2998-12-31T23:00:00Z",
"NextExpirationID": "",
"ObjectDescription": "12345678C: ",
"ObjectEntity": "Incident",
"ObjectTypeID": "T2",
"OwnerUserGroupID": "G66C",
"OwnerUserID": "S00000C",
"ParentRecurringTicketID": "",
"ParentTicketID": "",
"Problem": "",
"ProblemHTML": "",
"RemoteID": "",
"Score": 0,
"ServiceID": "",
"Site": null,
"SiteUnRead": true,
"Solicits": 0,
"SolutionHTML": "",
"SourceMailBoxID": "DEFAULT",
"Subject": "",
"SupplierID": "",
"Tag": "",
"TaskEffort": 0,
"TemplateTicketID": "",
"TicketClassificationID": "INC",
"TicketCode": "",
"TicketPriorityID": "P4",
"TicketSolutionID": "",
"TicketSourceID": "",
"TicketStatusID": "S3",
"TicketTypeID": "T2",
"UnRead": true,
"UrgencyID": "",
"UserID": ""
},
"requestToken": "token",
"result": {
"code": "0",
"desc": "",
"parameters": null,
"subcode": null
},
"success": true,
"total": 1
}
}
}
Human Readable Output
Ticket Created
Object Description Object Entity Ticket Classification ID Is New Expiration Date First Update User ID Owner User ID Date Assigned User ID 12345678C: Incident INC false 2023-10-04T15:45:00Z S00000C S00000C 2023-10-04T07:45:09Z S00000C
hda-list-tickets
List tickets.
Base Command
hda-list-tickets
Input
| Argument Name | Description | Required |
|---|---|---|
| ticket_id | The ID of the ticket to retrieve. | Optional |
| filter | Filters to apply to the ticket list. | Optional |
| page | The page number to retrieve. | Optional |
| page_size | The number of tickets per page. | Optional |
| limit | The maximum number of tickets to return. Default is 50. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| HelpdeskAdvanced.Ticket.AccountID | string | The account ID of the ticket. |
| HelpdeskAdvanced.Ticket.AssetID | string | The asset ID of the ticket. |
| HelpdeskAdvanced.Ticket.BilledTokens | string | The billed tokens of the ticket. |
| HelpdeskAdvanced.Ticket.CalendarID | string | The calendar ID of the ticket. |
| HelpdeskAdvanced.Ticket.ClosureDate | string | The closure date of the ticket. |
| HelpdeskAdvanced.Ticket.ContactID | string | The contact ID of the ticket. |
| HelpdeskAdvanced.Ticket.CustomerContractID | string | The customer contract ID of the ticket. |
| HelpdeskAdvanced.Ticket.Date | string | The date of the ticket. |
| HelpdeskAdvanced.Ticket.EstimatedTaskDuration | string | The estimated task duration of the ticket. |
| HelpdeskAdvanced.Ticket.EstimatedTaskStartDate | string | The estimated task start date of the ticket. |
| HelpdeskAdvanced.Ticket.ExpirationDate | string | The expiration date of the ticket. |
| HelpdeskAdvanced.Ticket.FirstUpdateUserID | string | The ID of the user who first updated the ticket. |
| HelpdeskAdvanced.Ticket.ID | string | The ID of the ticket. |
| HelpdeskAdvanced.Ticket.KnownIssue | string | The known issue field of the ticket. |
| HelpdeskAdvanced.Ticket.LanguageID | string | The language ID of the ticket. |
| HelpdeskAdvanced.Ticket.LastExpirationDate | string | The last expiration date of the ticket. |
| HelpdeskAdvanced.Ticket.LocationID | string | The location ID of the ticket. |
| HelpdeskAdvanced.Ticket.MailBoxID | string | The mailbox ID of the ticket. |
| HelpdeskAdvanced.Ticket.NextExpirationDate | string | The next expiration date of the ticket. |
| HelpdeskAdvanced.Ticket.NextExpirationID | string | The next expiration ID of the ticket. |
| HelpdeskAdvanced.Ticket.OwnerUserID | string | The owner user ID of the ticket. |
| HelpdeskAdvanced.Ticket.Problem | string | The problem field of the ticket. |
| HelpdeskAdvanced.Ticket.ProblemHTML | string | The problem field in HTML format of the ticket. |
| HelpdeskAdvanced.Ticket.Score | string | The score of the ticket. |
| HelpdeskAdvanced.Ticket.ServiceID | string | The service ID of the ticket. |
| HelpdeskAdvanced.Ticket.SiteUnRead | string | Whether the ticket is unread for the site. |
| HelpdeskAdvanced.Ticket.Solicits | string | Whether the ticket solicits feedback. |
| HelpdeskAdvanced.Ticket.Solution | string | The solution of the ticket. |
| HelpdeskAdvanced.Ticket.SolutionHTML | string | The solution in HTML format of the ticket. |
| HelpdeskAdvanced.Ticket.Subject | string | The subject of the ticket. |
| HelpdeskAdvanced.Ticket.SupplierID | string | The supplier ID of the ticket. |
| HelpdeskAdvanced.Ticket.TaskEffort | string | The task effort of the ticket. |
| HelpdeskAdvanced.Ticket.TicketClassificationID | string | The ticket classification ID of the ticket. |
| HelpdeskAdvanced.Ticket.TicketTypeID | string | The ticket type ID of the ticket. |
| HelpdeskAdvanced.Ticket.UrgencyID | string | The urgency ID of the ticket. |
Command example
#### Context Example
```json
{
"HelpdeskAdvanced": {
"Ticket": [
{
"AccountID": "C00C",
"AssetID": null,
"BilledTokens": 0,
"CalendarID": null,
"ClosureDate": "2019-04-28T07:04:19Z",
"ContactID": "S24007C",
"CustomerContractID": null,
"Date": "2019-04-25T07:03:48Z",
"EstimatedTaskDuration": 0,
"EstimatedTaskStartDate": null,
"ExpirationDate": "2019-04-28T15:03:00Z",
"FirstUpdateUserID": "S00000C",
"ID": "10000000C",
"KnownIssue": false,
"LanguageID": 5,
"LastExpirationDate": "2019-04-29T10:00:00Z",
"LocationID": "F3C",
"MailBoxID": "DEF_MBOX",
"NextExpirationDate": "2019-04-29T10:00:00Z",
"NextExpirationID": "244C",
"OwnerUserID": "S00000C",
"Problem": "Problem",
"ProblemHTML": "<p>Problem</p>",
"Score": 0,
"ServiceID": "S000X",
"SiteUnRead": null,
"Solicits": null,
"Solution": null,
"SolutionHTML": null,
"Subject": "Problem",
"SupplierID": null,
"TaskEffort": null,
"TicketClassificationID": "INC",
"TicketTypeID": "T4",
"UrgencyID": "U2"
}
]
}
}
Human Readable Output
Tickets
Ticket ID Subject Solution Date Service ID Problem Contact ID Owner User ID Account ID 10000000C Problem 2019-04-25T07:03:48Z S000X Problem S24007C S00000C C00C
hda-add-ticket-attachment
Add an attachment to a ticket.
Base Command
hda-add-ticket-attachment
Input
| Argument Name | Description | Required |
|---|---|---|
| ticket_id | The ticket ID to retrieve details for. | Required |
| entry_id | The file entry ID to attach. | Required |
Context Output
There is no context output for this command.
hda-list-ticket-attachments
List attachments for a ticket.
Base Command
hda-list-ticket-attachments
Input
| Argument Name | Description | Required |
|---|---|---|
| ticket_id | The ticket ID to retrieve details for. | Required |
| limit | The maximum number of tickets to return. Default is 50. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| HelpdeskAdvanced.Ticket.Attachment.BlobID | string | The blob ID of the attachment. |
| HelpdeskAdvanced.Ticket.Attachment.ContentType | string | The content type of the attachment. |
| HelpdeskAdvanced.Ticket.Attachment.Description | string | The description of the attachment. |
| HelpdeskAdvanced.Ticket.Attachment.EmailID | string | The email ID of the attachment. |
| HelpdeskAdvanced.Ticket.Attachment.FileName | string | The file name of the attachment. |
| HelpdeskAdvanced.Ticket.Attachment.FirstUpdate | string | The date and time when the attachment was first updated. |
| HelpdeskAdvanced.Ticket.Attachment.FirstUpdateUserID | string | The ID of the user who first uploaded the attachment. |
| HelpdeskAdvanced.Ticket.Attachment.ID | string | The ID of the attachment. |
| HelpdeskAdvanced.Ticket.Attachment.IsNew | string | Indicates whether the attachment is new. |
| HelpdeskAdvanced.Ticket.Attachment.KBSize | string | The size of the attachment in KB. |
| HelpdeskAdvanced.Ticket.Attachment.LastUpdate | string | The date and time when the attachment was last updated. |
| HelpdeskAdvanced.Ticket.Attachment.LastUpdateUserID | string | The ID of the user who last updated the attachment. |
| HelpdeskAdvanced.Ticket.Attachment.Note | string | Any additional notes about the attachment. |
| HelpdeskAdvanced.Ticket.Attachment.ObjectDescription | string | The description of the object the attachment is associated with. |
| HelpdeskAdvanced.Ticket.Attachment.ObjectEntity | string | The object entity of the attachment. |
| HelpdeskAdvanced.Ticket.Attachment.ObjectTypeID | string | The object type ID of the attachment. |
| HelpdeskAdvanced.Ticket.Attachment.OwnerUserID | string | The owner user ID of the attachment. |
| HelpdeskAdvanced.Ticket.Attachment.ParentObject | string | The parent object the attachment is associated with. |
| HelpdeskAdvanced.Ticket.Attachment.ParentObjectID | string | The ID of the parent object the attachment is associated with. |
| HelpdeskAdvanced.Ticket.Attachment.RemoteID | string | The remote ID of the attachment. |
| HelpdeskAdvanced.Ticket.Attachment.Site | string | The site ID the attachment is associated with. |
| HelpdeskAdvanced.Ticket.Attachment.TicketID | string | The ticket ID the attachment is associated with. |
| HelpdeskAdvanced.Ticket.Attachment.UniqueID | string | A unique ID for the attachment. |
Command example
!hda-list-ticket-attachments ticket_id=A0000001C
Context Example
{
"HelpdeskAdvanced": {
"Ticket": {
"Attachment": [
{
"BlobID": "A0000000C",
"ContentType": "text/plain",
"Description": "test.txt",
"EmailID": "",
"FileName": "test.txt",
"FirstUpdate": null,
"FirstUpdateUserID": "S00000C",
"ID": "A0000000C",
"IsNew": false,
"KBSize": 0,
"LastUpdate": "2023-09-29T08:30:04Z",
"LastUpdateUserID": "",
"Note": "",
"ObjectDescription": "test.txt",
"ObjectEntity": "Attachment",
"ObjectTypeID": "DEFAULT",
"OwnerUserID": "",
"ParentObject": "Incident",
"ParentObjectID": "A0000001C",
"RemoteID": "",
"Site": null,
"TicketID": "A0000001C",
"UniqueID": "00000000-0000-0000-0000-000000000000"
}
]
}
}
}
Human Readable Output
Attachments of A0000001C
File Name Last Update Description Object Description First Update User ID Object Entity Content Type test.txt 2023-09-29T08:30:04Z test.txt test.txt S00000C Attachment text/plain
hda-add-ticket-comment
Add a comment to a ticket.
Base Command
hda-add-ticket-comment
Input
| Argument Name | Description | Required |
|---|---|---|
| ticket_id | The ticket ID to retrieve details for. | Required |
| comment | The comment text. | Required |
| site_visible | Whether the ticket is visible to the customer site. Possible values are: True, False. Default is False. | Required |
Context Output
There is no context output for this command.
Command example
!hda-add-ticket-comment comment="here be comment" site_visible=True ticket_id=A0000001C
Human Readable Output
Comment was succesfully added to A0000001C
hda-list-ticket-statuses
List ticket statuses.
Base Command
hda-list-ticket-statuses
Input
| Argument Name | Description | Required |
|---|---|---|
| limit | The maximum number of statuses to return. Default is 50. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| HelpdeskAdvanced.TicketStatus.ID | Number | The status ID. |
| HelpdeskAdvanced.TicketStatus.Name | String | The status name. |
Command example
#### Context Example
```json
{
"HelpdeskAdvanced": {
"TicketStatus": [
{
"Description": "New",
"ID": "S1"
},
{
"Description": "Other",
"ID": "S10"
}
]
}
}
Human Readable Output
Results
Description ID New S1 Other S10
hda-change-ticket-status
Change the status of a ticket.
Base Command
hda-change-ticket-status
Input
| Argument Name | Description | Required |
|---|---|---|
| ticket_id | The ID of the ticket to change status for. | Required |
| status_id | The ID of the status to change the ticket to. | Required |
| note | An optional note to add with the status change. | Optional |
Context Output
There is no context output for this command.
Command example
!hda-change-ticket-status status_id=S2 ticket_id=1111111111C note=testing debug-mode=true
Human Readable Output
Changed status of ticket 1111111111C to S2 successfully.
hda-list-ticket-priorities
List ticket priorities.
Base Command
hda-list-ticket-priorities
Input
| Argument Name | Description | Required |
|---|---|---|
| ticket_id | The ID of the ticket to change status for. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| HelpdeskAdvanced.TicketPriority.ID | Number | The priority ID. |
| HelpdeskAdvanced.TicketPriority.Name | String | The priority name. |
hda-get-ticket-history
Get ticket history.
Base Command
hda-get-ticket-history
Input
| Argument Name | Description | Required |
|---|---|---|
| ticket_id | The ID of the ticket to change status for. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| HelpdeskAdvanced.TicketHistory.TicketID | string | The ticket ID associated with the history entry. |
| HelpdeskAdvanced.TicketHistory.AccountID | string | The account ID of the ticket. |
| HelpdeskAdvanced.TicketHistory.Attachments | string | List of attachments for the ticket. |
| HelpdeskAdvanced.TicketHistory.AutEmailCounter | string | The AUT email counter. |
| HelpdeskAdvanced.TicketHistory.ContactID | string | The contact ID of the ticket. |
| HelpdeskAdvanced.TicketHistory.Data.Comment | string | The comment text. |
| HelpdeskAdvanced.TicketHistory.Data.Date | string | The date of the history entry. |
| HelpdeskAdvanced.TicketHistory.Data.From | string | The user who made the change. |
| HelpdeskAdvanced.TicketHistory.Data.FromID | string | The ID of the user who made the change. |
| HelpdeskAdvanced.TicketHistory.Data.Problem | string | The problem associated with the history entry. |
| HelpdeskAdvanced.TicketHistory.Data.To | string | The user/team the ticket was assigned to. |
| HelpdeskAdvanced.TicketHistory.Data.ToID | string | The ID of the user/team the ticket was assigned to. |
| HelpdeskAdvanced.TicketHistory.ExternalAction | string | The external action associated with the history entry. |
| HelpdeskAdvanced.TicketHistory.FullName | string | The full name of the ticket. |
| HelpdeskAdvanced.TicketHistory.HistoryID | string | The ID of the history entry. |
| HelpdeskAdvanced.TicketHistory.OperationDescription | string | The description of the operation. |
| HelpdeskAdvanced.TicketHistory.OperationTypeID | string | The type ID of the operation. |
| HelpdeskAdvanced.TicketHistory.UpdateDate | date | The date the ticket was last updated. |
| HelpdeskAdvanced.TicketHistory.UserID | string | The user ID of the ticket. |
| HelpdeskAdvanced.TicketHistory.Username | string | The username associated with the ticket. |
Command example
!hda-get-ticket-history ticket_id=1111111C
Context Example
{
"HelpdeskAdvanced": {
"TicketHistory": {
"AccountID": "",
"Attachments": null,
"TicketID": "1111111C",
"AutEmailCounter": 0,
"ContactID": "",
"Data": {
"Comment": "testing",
"From": "Solved",
"FromID": "S6",
"To": "In Progress",
"ToID": "S2"
},
"ExternalAction": false,
"FullName": "John Doe",
"HistoryID": 5667526,
"OperationDescription": "Status change",
"OperationTypeID": 20,
"UpdateDate": "2023-10-04T07:45:35Z",
"UserID": "S00000C",
"Username": "username"
}
}
}
Human Readable Output
Ticket History: 1111111C
Update Date Ticket ID Operation Type ID Operation Description Full Name 2023-10-04T07:45:35Z 1111111C Status change John Doe
hda-list-users
List users.
Base Command
hda-list-users
Input
| Argument Name | Description | Required |
|---|---|---|
| user_id | The ID of the user to retrieve details for. | Optional |
| page | The page number. | Optional |
| page_size | The number of users to return per page. | Optional |
| limit | The maximum number of users to return per page. Default is 50. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| HelpdeskAdvanced.User.ID | string | The user ID. |
| HelpdeskAdvanced.User.Email | string | The user’s email address. |
| HelpdeskAdvanced.User.FirstName | string | The user’s first name. |
| HelpdeskAdvanced.User.LastName | string | The user’s last name. |
| HelpdeskAdvanced.User.Phone | string | The user’s phone number. |
Command example
#### Context Example
```json
{
"HelpdeskAdvanced": {
"User": [
{
"EMail": "Guest@example.com",
"FirstName": null,
"ID": "#GUEST#",
"LastName": "Guest",
"Phone": null
}
]
}
}
Human Readable Output
PAT HelpDeskAdvanced Users
Phone ID First Name Last Name E Mail #GUEST# Guest Guest@example.com
hda-list-groups
List groups.
Base Command
hda-list-groups
Input
| Argument Name | Description | Required |
|---|---|---|
| group_id | The ID of the group to retrieve details for. | Optional |
| page | The page number. | Optional |
| page_size | The number of groups to return per page. | Optional |
| limit | The maximum number of groups to return per page. Default is 50. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| HelpdeskAdvanced.Group.ID | string | The group ID. |
| HelpdeskAdvanced.Group.ObjectTypeID | string | The object type ID of the group. |
| HelpdeskAdvanced.Group.Description | string | The group description. |
Command example
#### Context Example
```json
{
"HelpdeskAdvanced": {
"Group": [
{
"Description": "Example",
"ID": "G000C",
"ObjectTypeID": "65"
}
]
}
}
Human Readable Output
PAT HelpDeskAdvanced Groups
Group ID Description Object Type ID G000C Example 65
Configuration parameters
base_url— PAT Helpdesk URL (required)credentials— Username (required)insecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (11)
-
hda-add-ticket-attachmentAdd an attachment to a ticket.
-
hda-add-ticket-commentAdd a comment to a ticket.
-
hda-change-ticket-statusChange the status of a ticket.
-
hda-create-ticketCreate a new ticket.
-
hda-get-ticket-historyGet ticket history.
-
hda-list-groupsList groups.
-
hda-list-ticket-attachmentsList attachments for a ticket.
-
hda-list-ticket-prioritiesList ticket priorities.
-
hda-list-ticket-statusesList ticket statuses.
-
hda-list-ticketsList tickets.
-
hda-list-usersList users.
from datetime import datetime, timezone from pathlib import Path from urllib.parse import unquote import freezegun import pytest from PATHelpdeskAdvanced import ( DATETIME_FORMAT, Client, DemistoException, Field, Filter, PaginateArgs, convert_response_dates, demisto, json, list_groups_command, list_ticket_attachments_command, list_users_command, paginate, pat_table_to_markdown, ) @pytest.mark.parametrize( "demisto_name, expected_demisto_name, expected_hda_name", [ ("incident_id", "incident_id", "IncidentID"), ("unread_email_html", "unread_email_html", "UnReadEmailHTML"), ("task", "task", "Task"), ("unread", "unread", "UnRead"), ("user_id_html", "user_id_html", "UserIDHTML"), ], ) def test_field(demisto_name, expected_demisto_name, expected_hda_name): """ Given a Demisto name, when initializing an instance of MyClass, then the demisto_name attribute should be set to the given Demisto name, and the hda_name attribute should be set to the expected HDA name. Args: demisto_name (str): The Demisto name. expected_demisto_name (str): The expected value of the demisto_name attribute. expected_hda_name (str): The expected value of the hda_name attribute. Returns: None """ field = Field(demisto_name) assert field.demisto_name == expected_demisto_name assert field.hda_name == expected_hda_name def test_converts_date_fields(): """ Given a response dict with date fields When convert_response_dates is called Then date fields are converted to datetime, and others are untouched """ EPOCH_2023_INT = 1693573200000 EPOCH_2022_INT = 1641042000000 STR_2023 = (datetime.fromtimestamp(EPOCH_2023_INT / 1000, tz=timezone.utc)).strftime(DATETIME_FORMAT) # noqa: UP017 STR_2022 = (datetime.fromtimestamp(EPOCH_2022_INT / 1000, tz=timezone.utc)).strftime(DATETIME_FORMAT) # noqa: UP017 raw = { "Date1": f"/Date({EPOCH_2023_INT})/", "Date2": [f"/Date({EPOCH_2023_INT})/", f"/Date({EPOCH_2022_INT})/", "🕒"], "other": [EPOCH_2023_INT, STR_2023], } result = convert_response_dates(raw) assert result["Date1"] == STR_2023 assert result["Date2"] == [STR_2023, STR_2022, "🕒"] assert result["other"] == raw["other"] @pytest.fixture def mocked_client(mocker): """Given a mocked Client instance""" return mocker.Mock() class TestCommands: @classmethod def test_create_ticket(cls, mocked_client): from PATHelpdeskAdvanced import create_ticket_command """ Given a client instance and arguments for creating a ticket When create_ticket_command is called with the client and args Then it should return a CommandResults instance with the expected outputs """ args = {"subject": "Test ticket", "problem": "Something went wrong"} mocked_client.create_ticket.return_value = { "id": 123, "data": { "ObjectDescription": "Test ticket", "ObjectEntity": "ticket", "Solution": "Fixed the issue", "TicketClassificationId": 1, "IsNew": True, "ExpirationDate": "/Date(1609459200000)/", "FirstUpdateUserId": 1, "OwnerUserId": 2, "Date": "/Date(1609459200000)/", "AssignedUserId": 2, }, } result = create_ticket_command(mocked_client, args) assert result.outputs == { "id": 123, "data": { "ObjectDescription": "Test ticket", "ObjectEntity": "ticket", "Solution": "Fixed the issue", "TicketClassificationId": 1, "IsNew": True, "ExpirationDate": "2021-01-01T00:00:00Z", "FirstUpdateUserId": 1, "OwnerUserId": 2, "Date": "2021-01-01T00:00:00Z", "AssignedUserId": 2, }, } assert result.raw_response == mocked_client.create_ticket.return_value assert result.readable_output == ( "### Ticket Created\n|Object Description|Object Entity|Solution|Is New|Expiration Date|Date|\n" "|---|---|---|---|---|---|\n| " "Test ticket | ticket | Fixed the issue | true | 2021-01-01T00:00:00Z | 2021-01-01T00:00:00Z |\n" ) @classmethod def test_get_ticket_history(cls, mocked_client): from PATHelpdeskAdvanced import get_ticket_history_command """ Given a client instance and arguments for getting a ticket's history When get_ticket_history is called with the client and args Then it should return a CommandResults instance with the expected outputs """ TICKET_ID = "dummy_ticket_id" history = [ { "AccountID": "", "Attachments": None, "AutEmailCounter": 0, "ContactID": "", "Data": { "Comment": "testing", "From": "Solved", "FromID": "S6", "To": "In Progress", "ToID": "S2", }, "ExternalAction": None, "FullName": "John Doe", "HistoryID": 5665556, "OperationDescription": "Status change", "OperationTypeID": 20, "UpdateDate": "2023-10-04T07:45:35Z", "UserID": "S00000C", "Username": "username", } ] mocked_client.get_ticket_history.return_value = history result = get_ticket_history_command(mocked_client, {"ticket_id": TICKET_ID}) assert result.outputs_prefix == "HelpdeskAdvanced.TicketHistory" assert result.outputs == [value | {"TicketID": TICKET_ID} for value in history] assert result.raw_response == history assert result.readable_output == ( "### Ticket History: dummy_ticket_id\n|Aut Email Counter|Data|Full Name|History ID|Operation Description|" "Operation Type ID|Update Date|User ID|Username|Ticket ID|\n|---|---|---|---|---|---|---|---|---|---|\n|" " | ***Comment***: testing<br>***From***: Solved<br>***FromID***: S6<br>***To***: In Progress<br>***ToID***:" " S2 | John Doe | | Status change | | 2023-10-04T07:45:35Z | S00000C | username | dummy_ticket_id |\n" ) @classmethod def test_list_tickets(cls, mocked_client): """ Given a client instance When list_tickets_command is called Then it should return CommandResults with expected outputs """ from PATHelpdeskAdvanced import list_tickets_command data = [ { "ExpirationDate": "/Date(1111111111111)/", "Subject": "Support Request", "Solution": "Solution text redacted", "LocationID": "XXX", "Solicits": None, "Date": "/Date(1111111111111)/", "FirstUpdateUserID": "AA", "BilledTokens": 0.0, "ServiceID": "XXX", "TicketTypeID": "XX", "Problem": "Problem description redacted", "SiteUnRead": None, "ClosureDate": "/Date(1111111111111)/", "ContactID": "XXX", "SupplierID": None, "CustomerContractID": None, "EstimatedTaskStartDate": None, "OwnerUserID": "AA", "Score": 0, "LastExpirationDate": "/Date(1111111111111)/", "KnownIssue": False, "MailBoxID": "XXX", "NextExpirationID": "XXX", "TicketClassificationID": "XXX", "CalendarID": None, "UrgencyID": "XX", "TaskEffort": None, "ProblemHTML": "Problem description redacted", "AccountID": "XXX", "NextExpirationDate": "/Date(1111111111111)/", "AssetID": "XXX", "ID": "10000002C", "SolutionHTML": "Solution text redacted", "EstimatedTaskDuration": 0, "LanguageID": 5, }, { "ExpirationDate": "/Date(1111111111111)/", "Subject": "On-site Support", "Solution": "Ticket correctly created through template.", "LocationID": "XXX", "Solicits": None, "Date": "/Date(1111111111111)/", "FirstUpdateUserID": "AA", "BilledTokens": 0.0, "ServiceID": "XXX", "TicketTypeID": "XX", "Problem": "Problem description redacted", "SiteUnRead": 0, "ClosureDate": "/Date(1111111111111)/", "ContactID": "XXX", "SupplierID": None, "CustomerContractID": None, "EstimatedTaskStartDate": None, "OwnerUserID": "XXX", "Score": 0, "LastExpirationDate": "/Date(1111111111111)/", "KnownIssue": False, "MailBoxID": "XXX", "NextExpirationID": None, "TicketClassificationID": "XXX", "CalendarID": None, "UrgencyID": None, "TaskEffort": None, "ProblemHTML": "Problem description redacted", "AccountID": "XXX", "NextExpirationDate": "/Date(1111111111111)/", "AssetID": None, "ID": "10000003C", "SolutionHTML": "Solution text redacted", "EstimatedTaskDuration": 0, }, ] mocked_client.list_tickets.return_value = {"data": data} result = list_tickets_command(mocked_client, {}) assert result.outputs == [ { "ExpirationDate": "2005-03-18T01:58:31Z", "Subject": "Support Request", "Solution": "Solution text redacted", "LocationID": "XXX", "Solicits": None, "Date": "2005-03-18T01:58:31Z", "FirstUpdateUserID": "AA", "BilledTokens": 0.0, "ServiceID": "XXX", "TicketTypeID": "XX", "Problem": "Problem description redacted", "SiteUnRead": None, "ClosureDate": "2005-03-18T01:58:31Z", "ContactID": "XXX", "SupplierID": None, "CustomerContractID": None, "EstimatedTaskStartDate": None, "OwnerUserID": "AA", "Score": 0, "LastExpirationDate": "2005-03-18T01:58:31Z", "KnownIssue": False, "MailBoxID": "XXX", "NextExpirationID": "XXX", "TicketClassificationID": "XXX", "CalendarID": None, "UrgencyID": "XX", "TaskEffort": None, "ProblemHTML": "Problem description redacted", "AccountID": "XXX", "NextExpirationDate": "2005-03-18T01:58:31Z", "AssetID": "XXX", "ID": "10000002C", "SolutionHTML": "Solution text redacted", "EstimatedTaskDuration": 0, "LanguageID": 5, }, { "ExpirationDate": "2005-03-18T01:58:31Z", "Subject": "On-site Support", "Solution": "Ticket correctly created through template.", "LocationID": "XXX", "Solicits": None, "Date": "2005-03-18T01:58:31Z", "FirstUpdateUserID": "AA", "BilledTokens": 0.0, "ServiceID": "XXX", "TicketTypeID": "XX", "Problem": "Problem description redacted", "SiteUnRead": 0, "ClosureDate": "2005-03-18T01:58:31Z", "ContactID": "XXX", "SupplierID": None, "CustomerContractID": None, "EstimatedTaskStartDate": None, "OwnerUserID": "XXX", "Score": 0, "LastExpirationDate": "2005-03-18T01:58:31Z", "KnownIssue": False, "MailBoxID": "XXX", "NextExpirationID": None, "TicketClassificationID": "XXX", "CalendarID": None, "UrgencyID": None, "TaskEffort": None, "ProblemHTML": "Problem description redacted", "AccountID": "XXX", "NextExpirationDate": "2005-03-18T01:58:31Z", "AssetID": None, "ID": "10000003C", "SolutionHTML": "Solution text redacted", "EstimatedTaskDuration": 0, }, ] assert result.outputs_prefix == "HelpdeskAdvanced.Ticket" assert result.outputs_key_field == "ID" assert result.raw_response == mocked_client.list_tickets.return_value assert ( result.readable_output == "\n".join( ( "### Tickets", "|Ticket ID|Subject|Solution|Date|Service ID|Problem|Contact ID|Owner User ID|Account ID|", "|---|---|---|---|---|---|---|---|---|", "| 10000002C | Support Request | Solution text redacted | 2005-03-18T01:58:31Z | XXX |" # continued next line " Problem description redacted | XXX | AA | XXX |", "| 10000003C | On-site Support | Ticket correctly created through template." # continued next line " | 2005-03-18T01:58:31Z | XXX | Problem description redacted | XXX | XXX | XXX |", "", ) ) ) class TestFilter: def test_filter_parse(self): """ Tests parsing a list of filter condition strings into Filter objects. Given A list of filter condition strings When Filter.parse_list() is called on the conditions Then The expected Filter objects are returned """ assert Filter.parse_list( ( '"id" eq "123"', '"name" lt "john"', '"nullvalue" ne null', '"notanullvalue" ne "none"', # quotes make it a string, not null ) ) == [ Filter(key="id", operator="eq", value="123"), Filter(key="name", operator="lt", value="john"), Filter(key="nullvalue", operator="ne", value=None), Filter(key="notanullvalue", operator="ne", value="none"), ] def test_dumps(self): """ Tests dumping a list of Filter objects to a JSON string. Given: A list of Filter objects with different key, operator and value. When: Filter.dumps_list() is called on the list of Filter objects. Then: The JSON string returned should match the expected format. """ assert Filter.dumps_list( ( Filter(key="id", operator="eq", value="123"), Filter(key="name", operator="lt", value="john"), Filter(key="nullvalue", operator="ne", value=None), Filter(key="notanullvalue", operator="ne", value="none"), ) ) == json.dumps( [ {"property": "id", "op": "eq", "value": "123"}, {"property": "name", "op": "lt", "value": "john"}, {"property": "nullvalue", "op": "ne", "value": None}, {"property": "notanullvalue", "op": "ne", "value": "none"}, ] ) def test_parse_invalid(self): """ Given an invalid filter condition string When parse_filter_conditions is called with a list containing the invalid string Then it should raise a DemistoException """ with pytest.raises(DemistoException): Filter.parse_list('"id" eq 123') # missing quotes around value class TestClient: base_url = "https://example.com" @staticmethod def dummy_client() -> Client: return Client( base_url=TestClient.base_url, username="test", password="test", verify=False, proxy=False, ) @classmethod def logged_in_client(cls, requests_mock_object): cls.mock_login_response(requests_mock_object) return cls.dummy_client() @staticmethod def mock_login_response(requests_mock_object): requests_mock_object.post( "https://example.com/Authentication/LoginEx?username=test&password=test", json={ "refreshToken": "refresh_token", "requestToken": "request_token", "expiresIn": 3300, "success": True, }, ) requests_mock_object.post( "https://example.com/Authentication/RefreshToken?token=refresh_token", json={ "refreshToken": "refresh_token", "requestToken": "request_token", "expiresIn": 3300, "success": True, }, ) @classmethod def test_client_login(cls, mocker, requests_mock) -> None: """ Given an expired refresh token in integration context When _reuse_or_create_token is called Then it should log in using username/password to get a new token """ # Mock expired token cls.mock_login_response(requests_mock) mocker.patch.object( demisto, "getIntegrationContext", return_value={}, ) client = TestClient.dummy_client() assert client.token.refresh_token == "refresh_token" @classmethod def test_expired_token(cls, mocker, requests_mock) -> None: """ Given an expired refresh token in integration context When _reuse_or_create_token is called Then it should log in using username/password to get a new token """ # Mock expired token requests_mock.post( "https://example.com/Authentication/LoginEx?username=test&password=test", json={ "refreshToken": "new_refresh_token", "requestToken": "new_request_token", "expiresIn": 3300, "success": True, }, ) mocker.patch.object( demisto, "getIntegrationContext", return_value={}, ) client = TestClient.dummy_client() assert client.token.refresh_token == "new_refresh_token" @classmethod @freezegun.freeze_time("2023-01-01 12:00:00") def test_reuse_valid_token(cls, mocker, requests_mock): """ Given a valid refresh token in integration context When _reuse_or_create_token is called Then it should reuse the existing valid token """ # Mock valid token mocker.patch.object( demisto, "getIntegrationContext", return_value={ "refresh_token": "previous_refresh_token", "expires_in": 3600, "token_expiry_utc": datetime(2023, 1, 1, 12, 30).isoformat(), }, ) requests_mock.post( "https://example.com/Authentication/RefreshToken?token=previous_refresh_token", json={ "refreshToken": "new_refresh_token", "requestToken": "new_request_token", "expiresIn": 3300, "success": True, }, ) client = TestClient.dummy_client() # Assert valid token reused assert client.token.refresh_token == "new_refresh_token" assert client.token.request_token == "new_request_token" @classmethod def test_add_ticket_attachment(cls, mocker, requests_mock): """ Given a client When calling add_ticket_attachment with two files Then make sure they are properly sent in the request NOTE: This tests the method, not the _command, as the method has non-trivial logic. """ from PATHelpdeskAdvanced import Path client = cls.logged_in_client(requests_mock) mocker.patch.object( demisto, "getFilePath", return_value={"path": "test.txt", "name": "test.txt"}, ) mocker.patch.object(Path, "open", return_value="mock file contents") mocked_request = requests_mock.post("https://example.com/Ticket/UploadNewAttachment", json={"success": True}) client.add_ticket_attachment(["1", "2"], ticket_id="ticket_id") assert mocked_request.called_once stringified_request = str(mocked_request.request_history[0]._request.body) for i in (1, 2): assert f'name="TicketAttachment_{i}"; filename="test.txt"\\r\\n\\r\\nmock file contents' in stringified_request @classmethod def test_list_groups(cls, requests_mock): """ Given a client and a mocked response When calling the list_groups_command with provided arguments Then test that the request is properly made, and the resuilt is properly returned """ mocked_request = requests_mock.post( f"{cls.base_url}/WSC/Projection", json={ "data": [ { "Description": "dummy group 1", "ID": "G001", "ObjectTypeID": "65", }, ], "success": True, }, ) result = list_groups_command( client=cls.logged_in_client(requests_mock), args={"group_id": "G001", "limit": "1", "page": "3", "page_size": "2"}, ) assert mocked_request.called_once assert unquote(mocked_request.request_history[0].query) == ( "entity=usergroup&columnexpressions=['id',+'description',+'objecttypeid']&" "columnnames=['id',+'description',+'objecttypeid']&start=6&limit=1&" 'filter=[{"property":+"id",+"op":+"eq",+"value":+"g001"}]' ) assert result.readable_output == ( "### PAT HelpDeskAdvanced Groups\n|Group ID|Description|Object Type ID|" "\n|---|---|---|\n| G001 | dummy group 1 | 65 |\n" ) assert result.outputs == [{"ID": "G001", "Description": "dummy group 1", "ObjectTypeID": "65"}] @classmethod def test_list_users(cls, requests_mock): """ Given a client and a mocked response When calling the list_users_command provided arguments Then test that the request is properly made, and the resuilt is properly returned """ mocked_request = requests_mock.post( f"{cls.base_url}/WSC/Projection", json={ "data": [ { "User.Phone": None, "ID": "U001", "User.FirstName": "HDA", "User.LastName": "Cortex XSOAR", "User.EMail": None, } ], "success": True, }, ) result = list_users_command( client=cls.logged_in_client(requests_mock), args={"user_id": "U001", "limit": "1", "page": "3", "page_size": "2"}, ) assert mocked_request.called_once assert unquote(mocked_request.request_history[0].query) == ( "entity=users&columnexpressions=['id',+'user.firstname',+'user.lastname',+'user.email'," "+'user.phone',+'user.mobile']&columnnames=['id',+'user.firstname',+'user.lastname',+'user.email'," '+\'user.phone\',+\'user.mobile\']&start=6&limit=1&filter=[{"property":+"id",+"op":+"eq",+"value":+"u001"}]' ) assert result.readable_output == ( "### PAT HelpDeskAdvanced Users\n|Phone|ID|First Name|Last Name|E Mail|" "\n|---|---|---|---|---|\n| | U001 | HDA | Cortex XSOAR | |\n" ) assert result.outputs == [ { "Phone": None, "ID": "U001", "FirstName": "HDA", "LastName": "Cortex XSOAR", "EMail": None, } ] @classmethod def test_list_ticket_attachments(cls, requests_mock): """ Given a client and a mocked response When calling the list_users_command provided arguments Then test that the request is properly made, and the resuilt is properly returned """ mocked_request = requests_mock.post( f"{cls.base_url}/WSC/List", json={ "data": [ { "ObjectDescription": "Untitled document.pdf", "ObjectEntity": "Attachment", "LastUpdateUserID": "", "IsNew": False, "KBSize": 0.76, "ObjectTypeID": "DEFAULT", "ID": "A12345C", "UniqueID": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaaaaaa", "Note": "", "FileName": "Untitled document.pdf", "FirstUpdate": None, "TicketID": "1234567C", "ContentType": "application/pdf", "OwnerUserID": "", "ParentObject": "Incident", "ParentObjectID": "1234567C", "Site": None, "EmailID": "", "BlobID": "A12345C", "FirstUpdateUserID": "S44444C", "RemoteID": "", "LastUpdate": "2023-08-02T00:00:00Z", "Description": "Untitled document.pdf", } ], "total": 5, "result": { "code": "0", "subcode": None, "parameters": None, "desc": "", }, "success": True, "requestToken": "token", }, ) result = list_ticket_attachments_command( client=cls.logged_in_client(requests_mock), args={"ticket_id": "A12345C", "limit": "1"}, ) assert mocked_request.called_once assert unquote(mocked_request.request_history[0].query) == ( 'entity=attachments&start=0&limit=1&filter=[{"property":+"parentobject",+"op":+"eq",+"value":+"ticket"},' '+{"property":+"parentobjectid",+"op":+"eq",+"value":+"a12345c"}]' ) assert result.readable_output == ( "### Attachments of A12345C\n|File Name|Last Update|Description|Object Description|First Update User ID|Object Entity" "|Content Type|\n|---|---|---|---|---|---|---|\n| Untitled document.pdf " "| 2023-08-02T00:00:00Z | Untitled document.pdf" " | Untitled document.pdf | S44444C | Attachment | application/pdf |\n" ) assert result.outputs == [ { "ObjectDescription": "Untitled document.pdf", "ObjectEntity": "Attachment", "LastUpdateUserID": "", "IsNew": False, "KBSize": 0.76, "ObjectTypeID": "DEFAULT", "ID": "A12345C", "UniqueID": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaaaaaa", "Note": "", "FileName": "Untitled document.pdf", "FirstUpdate": None, "TicketID": "1234567C", "ContentType": "application/pdf", "OwnerUserID": "", "ParentObject": "Incident", "ParentObjectID": "1234567C", "Site": None, "EmailID": "", "BlobID": "A12345C", "FirstUpdateUserID": "S44444C", "RemoteID": "", "LastUpdate": "2023-08-02T00:00:00Z", "Description": "Untitled document.pdf", } ] @classmethod def test_non_json_response(cls, requests_mock): client = cls.logged_in_client(requests_mock) requests_mock.post( "https://example.com/WSC/Projection?entity=UserGroup&columnExpressions=%5B%27ID%27" "%2C+%27Description%27%2C+%27ObjectTypeID" "%27%5D&columnNames=%5B%27ID%27%2C+%27Description%27%2C+%27ObjectTypeID%27%5D&start=0&limit=1", text="surprise", ) with pytest.raises(ValueError, match="API returned non-JSON response: surprise"): client.list_groups(limit=1) @classmethod @pytest.mark.parametrize( "response_file,expected_error_message", ( pytest.param( "response_401.html", "Server Error. 401 - Unauthorized: Access is denied due to invalid credentials", id="401", ), pytest.param( "response_invalid_request.html", "A server error has occurred. Please, contact portal administrator.", id="invalid response", ), ), ) def test_error_response(cls, requests_mock, response_file: str, expected_error_message: str): """ Given a client and an invalid response When calling a method that simulates these responses Then make sure the response is parsed well """ client = cls.logged_in_client(requests_mock) requests_mock.post( "https://example.com/WSC/Projection?entity=UserGroup&columnExpressions=%5B%27ID%27%2C+%27Description%27%2C+%" "27ObjectTypeID%27%5D&columnNames=%5B%27ID%27%2C+%27Description%27%2C+%27ObjectTypeID%27%5D&start=0&limit=1", text=Path(f"test_data/{response_file}").read_text(), ) with pytest.raises(DemistoException, match=expected_error_message): client.list_groups(limit=1) class TestPaginate: @staticmethod def test_paginate_with_limit_only(): """ Given limit is provided When paginate is called with only limit Then it returns start as 0 and provided limit """ assert paginate(limit=10) == PaginateArgs(start=0, limit=10) @staticmethod def test_paginate_with_page_and_page_size(): """ Given limit is provided When paginate is called with paging arguments Then it returns start as 0 and provided limit """ assert paginate(page=2, page_size=3, limit=3) == PaginateArgs(start=6, limit=3) @staticmethod def test_paginate_with_invalid_args(): """ Given only one of page or page_size is provided When paginate is called with invalid args Then it raises an appropriate error """ with pytest.raises(ValueError): paginate(page=1, limit="None") with pytest.raises(KeyError): paginate(page_size=10) @staticmethod def test_paginate_with_non_int_args(): """ Given page and page_size are non-integer values When paginate is called with non-int args Then it raises a DemistoException """ with pytest.raises(ValueError): paginate(page="a", page_size="b", limit=3) class TestPatToMarkdown: @staticmethod def test_pat_table_to_markdown_with_no_fields(): """ Given a title, output dict, and no fields When pat_table_to_markdown is called Then it returns markdown string of the full output """ title = "Test Title" output = {"key1": "value1", "key2": "value2"} assert pat_table_to_markdown(title, output, None) == "### Test Title\n|Key 1|Key 2|\n|---|---|\n| value1 | value2 |\n" @staticmethod def test_pat_table_to_markdown_with_fields(): """ Given title, output, and specified fields When pat_table_to_markdown is called Then it returns markdown string of filtered outputs """ title = "Test Title" output = {"Field1": "1", "Field2": "2", "Field3": "3"} fields = (Field("field1"), Field("field2")) assert pat_table_to_markdown(title, output, fields) == "### Test Title\n|Field 1|Field 2|\n|---|---|\n| 1 | 2 |\n" @staticmethod def test_pat_table_to_markdown_with_field_replacements(): """ Given title, output dict, fields, and field replacements When pat_table_to_markdown is called with field_replacements Then it returns markdown string with replaced field names """ title = "Test Title" replace_me = Field("replace_me") untouched = Field("untouched") replaced = Field("replaced") output = {replace_me.hda_name: "1", untouched.hda_name: "2"} assert ( pat_table_to_markdown(title, output, fields=None, field_replacements={replace_me: replaced}) == "### Test Title\n|Replaced|Untouched|\n|---|---|\n| 1 | 2 |\n" )