BMCHelixRemedyforce
BMC Helix Remedyforce integration enables customers to create/update service requests and incidents, update statuses, and resolve service requests and incidents with customer notes. This integration exposes standard ticketing capabilities that can be utilized as part of automation & orchestration.
Utilities · Bmc Helix Remedyforce
Details
| ID | BMCHelixRemedyforce |
|---|---|
| Provider | BMC Software |
| Category | Utilities |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
BMC Helix Remedyforce integration allows customers to create/update service requests and incidents. It also allows to update status, resolve service requests and incidents with customer notes. This integration exposes standard ticketing capabilities that can be utilized as part of automation & orchestration.
This integration was integrated and tested with version 202002.79 of BMC Helix Remedyforce.
Configure BMC Helix Remedyforce in Cortex
| Parameter | Description | Required |
|---|---|---|
| url | BMC Remedyforce URL (e.g. https://example.com) | True |
| username | Username | True |
| password | Password | True |
| type | Type | False |
| category | Category | False |
| impact | Impact | False |
| urgency | Urgency | False |
| status | Status | False |
| queue | Queue | False |
| max_incidents | Max Incidents | False |
| fetch_note | Fetch Note(s) | False |
| query | Query (If provided other filtering parameters will be ignored) e.g. select Name, LastModifiedDate from BMCServiceDesk__Incident__c where BMCServiceDesk__Impact_Id__c = ‘High’ | False |
| incidentType | Incident type | False |
| insecure | Trust any certificate (not secure) | False |
| proxy | Use system proxy settings | False |
| request_timeout | HTTP(S) Request Timeout (in seconds) | True |
| isFetch | Fetch incidents | False |
| firstFetchTimestamp | First Fetch Timestamp (<number> <time unit>, e.g., 12 hours, 7 days, 3 months, 1 year) | 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.
bmc-remedy-service-request-definition-get
This command gets details of service request definitions with the help of service request definition name.
Base Command
bmc-remedy-service-request-definition-get
Input
| Argument Name | Description | Required |
|---|---|---|
| service_request_definition_name | service_request_definition_name is the name of the service request definition whose details the user wants to get. If any value is not specified, it gets details of all service request definitions. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.ServiceRequestDefinition.Id | String | Service Request Definition Id. |
| BmcRemedyforce.ServiceRequestDefinition.CategoryId | String | Category Id of the Service Request Definition. |
| BmcRemedyforce.ServiceRequestDefinition.IsProblem | Boolean | Indicates if it is a problem or not. |
| BmcRemedyforce.ServiceRequestDefinition.LastModifiedDate | Date | Last modified date. |
| BmcRemedyforce.ServiceRequestDefinition.CreatedDate | Date | Created Date. |
| BmcRemedyforce.ServiceRequestDefinition.Questions.Id | String | Id of question attached with Service Request Definition. |
| BmcRemedyforce.ServiceRequestDefinition.Questions.IsRequired | Boolean | Indicates if a question is required or not. |
| BmcRemedyforce.ServiceRequestDefinition.Questions.Type | String | Type of the question. |
| BmcRemedyforce.ServiceRequestDefinition.Questions.Text | String | Name of the question. |
| BmcRemedyforce.ServiceRequestDefinition.Conditions.SRDId | String | Service Request Definition Id of condition. |
| BmcRemedyforce.ServiceRequestDefinition.Conditions.Value | String | Value of the condition. |
| BmcRemedyforce.ServiceRequestDefinition.Conditions.Operator | String | Operator of the condition. |
| BmcRemedyforce.ServiceRequestDefinition.Conditions.DependentQuestionId | String | Question Id of dependent question which is associated with the condition. |
| BmcRemedyforce.ServiceRequestDefinition.Conditions.Id | String | Id of the condition. |
| BmcRemedyforce.ServiceRequestDefinition.Conditions.QuestionId | String | Associate question Id for the condition. |
| BmcRemedyforce.ServiceRequestDefinition.requestFor | String | Request for field value. |
| BmcRemedyforce.ServiceRequestDefinition.requestedBy | String | Requested by field value. |
| BmcRemedyforce.ServiceRequestDefinition.title | String | Title field value for Service Request Definition. |
| BmcRemedyforce.ServiceRequestDefinition.description | String | Description field value for Service Request Definition. |
| BmcRemedyforce.ServiceRequestDefinition.approvalRequired | Boolean | Indicates if approval is required or not. |
Command Example
!bmc-remedy-service-request-definition-get service_request_definition_name="Ask Benefits/HR Question"
Context Example
{
"BmcRemedyforce": {
"ServiceRequestDefinition": {
"CategoryId": "a212w000000UFftAAG",
"CreatedDate": "2020-07-14 06:02:49",
"Id": "a3H2w000000H5UhEAK",
"IsProblem": false,
"LastModifiedDate": "2020-07-14 06:02:49",
"Questions": [
{
"Id": "a3D2w000000TmAKEA0",
"IsRequired": false,
"Text": "Benefits/HR Question Details",
"Type": "header section"
},
{
"Id": "a3D2w000000TmALEA0",
"IsRequired": true,
"Text": "Please provide details on the information you are requesting",
"Type": "textarea"
}
],
"approvalRequired": false,
"description": "Service Request for information pertaining to Benefits/HR",
"email": "testuser@mail.com",
"requestFor": "Test user",
"requestedBy": "Test user",
"title": "Ask Benefits/HR Question"
}
}
}
Human Readable Output
Total retrieved service request definition(s): 1
Service Request Definition Id Service Request Definition Name Questions a3H2w000000H5UhEAK Ask Benefits/HR Question Id: a3D2w000000TmAKEA0
Question: Benefits/HR Question Details
Is Required: No
Id: a3D2w000000TmALEA0
Question: Please provide details on the information you are requesting
Is Required: Yes
bmc-remedy-note-create
This command creates notes for incidents and service requests.
Base Command
bmc-remedy-note-create
Input
| Argument Name | Description | Required |
|---|---|---|
| request_number | The unique incident or service request number that needs to be updated with a note. | Required |
| summary | Description of incident or service request while creating the note. Default value of summary is ‘Client Note’. |
Optional |
| note | Detailed note for incident or service request. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.Note.Id | String | Id of the created note. |
| BmcRemedyforce.Note.WorkInfoType | String | Work Information of note. |
| BmcRemedyforce.Note.ViewAccess | String | View Access Information of note. |
| BmcRemedyforce.Note.Summary | String | The description of the note. |
| BmcRemedyforce.Note.Submitter | String | User who added note. |
| BmcRemedyforce.Note.srId | String | Id of the Service Request/Incident on which we have added note. |
| BmcRemedyforce.Note.Notes | String | Note for Service Request/Incident. |
| BmcRemedyforce.Note.ModifiedDate | Date | Last modified date for note. |
| BmcRemedyforce.Note.CreatedDate | Date | Created date of note. |
Command Example
!bmc-remedy-note-create request_number=SR00000054 summary="demo note" note="demo note"
Context Example
{
"BmcRemedyforce": {
"Note": {
"CreatedDate": "2020-07-30 09:25:42",
"Id": "a2N2w000000Y39FEAS",
"ModifiedDate": "2020-07-30 09:25:42",
"Notes": "demo note\r\ntestuser@bmcremedyforce.com 7/30/2020 2:25 AM",
"Submitter": "Test user",
"Summary": "Test user's summary",
"srId": "a2U2w000000YGQREA4"
}
}
}
Human Readable Output
The service request/incident SR00000054 is successfully updated with the note.
bmc-remedy-service-request-update
This command updates the details of a service request for a given service request number.
Base Command
bmc-remedy-service-request-update
Input
| Argument Name | Description | Required |
|---|---|---|
| service_request_number | The unique number of the service request for which details needs to be updated. | Required |
| category_id | category_id is the unique Id of the category. Categories allow users to classify the incident or service request using standard classifications to track the reporting purposes. Users can get the category Id from the category name using ‘bmc-remedy-category-details-get’ command. |
Optional |
| queue_id | queue_id is the unique Id of the owner. Users can get the queue Id from the owner name using ‘bmc-remedy-queue-details-get’ command. |
Optional |
| staff_id | staff_id is the unique Id of the staff to whom the user wants to assign the record. Users can get the staff Id from the staff details using ‘bmc-remedy-user-details-get’ command. |
Optional |
| status_id | status_id is the unique Id of the status that is used to display the progress of the service request through its stages of opening to closure. Users can get the status Id from the status name using ‘bmc-remedy-status-details-get’ command. |
Optional |
| urgency_id | urgency_id is the unique Id of the urgency which is used to determine the priority of the service request. Users can get the urgency Id from the urgency name using ‘bmc-remedy-urgency-details-get’ command. |
Optional |
| client_id | client_id is the unique Id of the client. It helps to select a client for a particular service request. Users can get the client Id from the email using ‘bmc-remedy-user-details-get’ command. |
Optional |
| additional_fields | The fields which are not present in the current argument list can be added here in the format “fieldname1=value;fieldname2=value”. Possible fields: impact_id, account_id or any other custom field. |
Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.ServiceRequest.Number | String | Service request number. |
| BmcRemedyforce.ServiceRequest.Id | String | Service request Id. |
| BmcRemedyforce.ServiceRequest.LastUpdatedDate | String | Last updated date & time of a service request. |
Command Example
!bmc-remedy-service-request-update service_request_number=SR00000054 status_id=a3w2w000000TfGlAAK category_id=a212w000000UFfyAAG aditional_fields="impact_id=HIGH;asset_id=a0K2w000000wi2uEAA"
Context Example
{
"BmcRemedyforce": {
"ServiceRequest": {
"Id": "a2U2w000000YGQREA4",
"LastUpdatedDate": "2020-08-01T12:05:48Z",
"Number": "00000054"
}
}
}
Human Readable Output
The service request 00000054 is successfully updated.
bmc-remedy-service-request-create
This command allows the user to create a new service request. A service request is the request record that is generated from the service request definition to manage and track the execution.
Base Command
bmc-remedy-service-request-create
Input
| Argument Name | Description | Required |
|---|---|---|
| service_request_definition_id | The unique Id of the service request definition. Users can get the service request definition Id from service request definition name using ‘bmc-remedy-service-request-definition-get’ command. |
Required |
| service_request_definition_params | Each service request definition expects specific parameters to be supplied. Specify the parameters as a delimiter (;) separated string. Example: “param1=value1; param2=value2”. |
Optional |
| client_id | client_id is the unique Id of the client. It helps to select a client for a particular service request. Users can get the client Id from the email using ‘bmc-remedy-user-details-get’ command. |
Optional |
| category_id | category_id is the unique Id of the category. Categories allow users to classify the incident or service request using standard classifications to track the reporting purposes. Users can get the category Id from the category name using ‘bmc-remedy-category-details-get’ command. |
Optional |
| queue_id | queue_id is the unique Id of the owner. Users can get the queue Id from the owner name using ‘bmc-remedy-queue-details-get’ command. |
Optional |
| staff_id | staff_id is the unique Id of the staff to whom the user wants to assign the record. Users can get the staff Id from the staff details using ‘bmc-remedy-user-details-get’ command. |
Optional |
| urgency_id | urgency_id is the unique Id of the urgency which is used to determine the priority of the service request. Users can get the urgency Id from the urgency name using ‘bmc-remedy-urgency-details-get’ command. |
Optional |
| status_id | status_id is the unique Id of the status that is used to display the progress of the service request through its stages of opening to closure. Users can get the status Id from the status name using ‘bmc-remedy-status-details-get’ command. |
Optional |
| additional_fields | The fields which are not present in the current argument list can be added here in the format “fieldname1=value;fieldname2=value”. Possible fields: impact_id, account_id or any other custom field. |
Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.ServiceRequest.Number | String | Service request number. |
| BmcRemedyforce.ServiceRequest.Id | String | Service request Id. |
| BmcRemedyforce.ServiceRequest.CreatedDate | String | Creation date & time of service request. |
Command Example
!bmc-remedy-service-request-create service_request_definition_id=a3H2w000000H5UVEA0 category_id=a212w000000UFfyAAG status_id=a3w2w000000TfGBAA0
Context Example
{
"BmcRemedyforce": {
"ServiceRequest": {
"CreatedDate": "2020-08-01T12:05:13Z",
"Id": "a2U2w000000YTEhEAO",
"Number": "00012192"
}
}
}
Human Readable Output
The service request 00012192 is successfully created.
bmc-remedy-template-details-get
This command helps to get template details for Incidents. Templates enable users to prepopulate commonly used fields in a form.
Base Command
bmc-remedy-template-details-get
Input
| Argument Name | Description | Required |
|---|---|---|
| template_name | template_name is the name of the template whose details the user wants to get. If any value is not specified, it gets details of all the available templates. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.Template.Id | String | Template Id. |
| BmcRemedyforce.Template.Name | String | Template name. |
| BmcRemedyforce.Template.Description | String | Template description. |
| BmcRemedyforce.Template.Recurring | Boolean | Recurrence of template. |
Command Example
!bmc-remedy-template-details-get template_name="General Support - SR"
Context Example
{
"BmcRemedyforce": {
"Template": {
"Description": "Used for the General Support Service Request to capture any issues or requests that may not yet be covered by published SRs.",
"Id": "a3k2w000000LSl9AAG",
"Name": "General Support - SR",
"Recurring": false
}
}
}
Human Readable Output
Total retrieved template(s): 1
Id Name Description Recurring a3k2w000000LSl9AAG General Support - SR Used for the General Support Service Request to capture any issues or requests that may not yet be covered by published SRs. false
bmc-remedy-impact-details-get
This command helps to get impact details for incidents and service requests. Impact helps to calculate priority of the incident or service request.
Base Command
bmc-remedy-impact-details-get
Input
| Argument Name | Description | Required |
|---|---|---|
| impact_name | impact_name is the name of the impact whose details the user wants to get. If any value is not specified, it gets details of all the available impacts. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.Impact.Id | String | Impact Id. |
| BmcRemedyforce.Impact.Name | String | Impact Name. |
Command Example
!bmc-remedy-impact-details-get impact_name=HIGH
Context Example
{
"BmcRemedyforce": {
"Impact": {
"Id": "a2M2w000000UApmEAG",
"Name": "HIGH"
}
}
}
Human Readable Output
Total retrieved impact(s): 1
Id Name a2M2w000000UApmEAG HIGH
bmc-remedy-service-offering-details-get
This command helps to get service offering details for incidents. Users can link a service offering of the associated service.
Base Command
bmc-remedy-service-offering-details-get
Input
| Argument Name | Description | Required |
|---|---|---|
| service_offering_name | service_offering_name is the name of service offering whose details the user wants to get. If any value is not specified, it gets details of all the available service offerings. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.ServiceOffering.Id | String | Service offering Id. |
| BmcRemedyforce.ServiceOffering.Name | String | Service offering name. |
Command Example
!bmc-remedy-service-offering-details-get service_offering_name="Building Access"
Context Example
{
"BmcRemedyforce": {
"ServiceOffering": {
"Id": "a0K2w000000wi3FEAQ",
"Name": "Building Access"
}
}
}
Human Readable Output
Total retrieved service offering(s): 1
Id Name a0K2w000000wi3FEAQ Building Access
bmc-remedy-incident-create
This command allows the user to create a new incident.
Base Command
bmc-remedy-incident-create
Input
| Argument Name | Description | Required |
|---|---|---|
| client_id | client_id is the unique Id of the client. It helps to select a client for a particular Incident. Users can get the client Id from the email using ‘bmc-remedy-user-details-get’ command. |
Required |
| description | This field represents the description of the incident that the user wants to create. | Optional |
| opened_date | opened_date is the date and time at which the incident was created. Use the yyyy-MM-ddTHH:mm:ss.SSS+/-HHmm or yyyy-MM-ddTHH:mm:ss.SSSZ formats to specify dateTime fields. | Optional |
| due_date | due_date is the date and time at which the incident should be completed. Use the yyyy-MM-ddTHH:mm:ss.SSS+/-HHmm or yyyy-MM-ddTHH:mm:ss.SSSZ formats to specify dateTime fields. | Optional |
| queue_id | queue_id is the unique Id of the owner. Users can get the queue Id from the owner name using ‘bmc-remedy-queue-details-get’ command. |
Optional |
| template_id | template_id is the unique Id of the template. Templates enable users to pre-populate commonly used fields in a form. Users can get the template Id from the template name using ‘bmc-remedy-template-details-get’ command. |
Optional |
| category_id | category_id is the unique Id of the category. Categories allow users to classify the incident or service request using standard classifications to track the reporting purposes. Users can get the category Id from the category name using ‘bmc-remedy-category-details-get’ command. |
Optional |
| urgency_id | urgency_id is the unique Id of the urgency which is used to determine the priority of the incident. Users can get the urgency Id from the urgency name using ‘bmc-remedy-urgency-details-get’ command. |
Optional |
| status_id | status_id is the unique Id of the status that is used to display the progress of the incident through its stages of opening to closure. Users can get the status Id from the status name using ‘bmc-remedy-status-details-get’ command. | Optional |
| staff_id | staff_id is the unique Id of the staff to whom the user wants to assign the record. Users can get the staff Id from the staff details using ‘bmc-remedy-user-details-get’ command. |
Optional |
| additional_fields | The fields which are not present in the current argument list can be added here in the format “fieldname1=value;fieldname2=value”. Possible fields: broadcast_id, service_id,impact_id, service_offering_id, asset_id, outage_start, outage_end, account_id or any other custom field. |
Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.Incident.Id | String | Incident Id. |
| BmcRemedyforce.Incident.Number | String | Incident number. |
| BmcRemedyforce.Incident.CreatedDate | String | Creation date & time of Incident. |
Command Example
!bmc-remedy-incident-create client_id=0052w000004Z9vJAAS status_id=a3w2w000000TfGlAAK category_id=a212w000000UFfyAAG
Context Example
{
"BmcRemedyforce": {
"Incident": {
"CreatedDate": "2020-08-01 12:06:08",
"Id": "a2U2w000000YTEmEAO",
"Number": "00012193"
}
}
}
Human Readable Output
The incident 00012193 is successfully created.
bmc-remedy-incident-update
This command updates the details of an incident for a given incident number.
Base Command
bmc-remedy-incident-update
Input
| Argument Name | Description | Required |
|---|---|---|
| incident_number | The unique number of the incident whose details need to be updated. | Required |
| category_id | category_id is the unique Id of the category. Categories allow users to classify the incident or service request using standard classifications to track the reporting purposes. Users can get the category Id from the category name using ‘bmc-remedy-category-details-get’ command. |
Optional |
| queue_id | queue_id is the unique Id of the owner. Users can get the queue Id from the owner name using ‘bmc-remedy-queue-details-get’ command. |
Optional |
| staff_id | staff_id is the unique Id of the staff to whom the user wants to assign the record. Users can get the staff Id from the staff details using ‘bmc-remedy-user-details-get’ command. |
Optional |
| status_id | status_id is the unique Id of the status that is used to display the progress of the incident through its stages of opening to closure. Users can get the status Id from the status name using ‘bmc-remedy-status-details-get’ command. |
Optional |
| urgency_id | urgency_id is the unique Id of the urgency which is used to determine the priority of the incident. Users can get the urgency Id from the urgency name using ‘bmc-remedy-urgency-details-get’ command. |
Optional |
| due_date | due_date is the date and time until which the incident should be completed. Use the yyyy-MM-ddTHH:mm:ss.SSS+/-HHmm or yyyy-MM-ddTHH:mm:ss.SSSZ formats to specify dateTime fields. | Optional |
| client_id | client_id is the unique Id of the client. It helps to select the client for a particular incident. Users can get the client Id from the email using ‘bmc-remedy-user-details-get’ command. |
Optional |
| additional_fields | The fields which are not present in the current argument list can be added here in the format “fieldname1=value;fieldname2=value”. Possible fields: broadcast_id, service_id,impact_id, service_offering_id, asset_id, outage_start, outage_end, account_id or any other custom field. |
Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.Incident.Id | String | Incident Id. |
| BmcRemedyforce.Incident.Number | String | Incident number. |
| BmcRemedyforce.Incident.LastUpdatedDate | String | Last updated date & time of Incident. |
Command Example
!bmc-remedy-incident-update incident_number=IN00000182 status_id=a3w2w000000TfGlAAK category_id=a212w000000UFfyAAG aditional_fields="impact_id=HIGH;asset_id=a0K2w000000wi2uEAA" using=BMCHelixRemedyforce_instance_1
Context Example
{
"BmcRemedyforce": {
"Incident": {
"Id": "a2U2w000000YGaHEAW",
"LastUpdatedDate": "2020-08-01T12:05:37Z",
"Number": "00000182"
}
}
}
Human Readable Output
The incident 00000182 is successfully updated.
bmc-remedy-asset-details-get
This command helps to get asset or configuration item details for incidents.
Base Command
bmc-remedy-asset-details-get
Input
| Argument Name | Description | Required |
|---|---|---|
| asset_name | asset_name is the name of the asset for which the command will find the metadata. | Optional |
| instance_type | Assets belonging to the selected instance_type will be populated. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.Asset.Id | String | Asset Id. |
| BmcRemedyforce.Asset.Name | String | Asset name. |
| BmcRemedyforce.Asset.Description | String | Asset description. |
| BmcRemedyforce.Asset.Class_Name | String | Asset under BMC class type. |
| BmcRemedyforce.Asset.Asset_Number | String | Asset number. |
| BmcRemedyforce.Asset.Instance_Type | String | Assets belonging to selected class. |
Command Example
!bmc-remedy-asset-details-get asset_name=BlackBerry-Houston01
Context Example
{
"BmcRemedyforce": {
"Asset": {
"Asset_Number": "1102942",
"Class_Name": "BMC_ComputerSystem",
"Description": "Houston BlackBerry Server",
"Id": "a0K2w000000wi1bEAA",
"Instance_Type": "CI / Asset",
"Name": "BlackBerry-Houston01"
}
}
}
Human Readable Output
Total retrieved asset(s): 1
Id Name Description Asset # Class Name Instance Type a0K2w000000wi1bEAA BlackBerry-Houston01 Houston BlackBerry Server 1102942 BMC_ComputerSystem CI / Asset
bmc-remedy-account-details-get
This command helps to get account details for incidents and service requests.
Base Command
bmc-remedy-account-details-get
Input
| Argument Name | Description | Required |
|---|---|---|
| account_name | account_name is the account name of the account whose details the user wants to get. If any value is not specified, it gets details of all the available accounts. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.Account.Id | String | Account Id. |
| BmcRemedyforce.Account.Name | String | Account name. |
Command Example
!bmc-remedy-account-details-get account_name="test_account"
Context Example
{
"BmcRemedyforce": {
"Account": {
"Id": "0012w00000GMthNAAT",
"Name": "test_account"
}
}
}
Human Readable Output
Total retrieved account(s): 1
Id Name 0012w00000GMthNAAT test_account
bmc-remedy-status-details-get
This command helps to get status details for incidents and service requests. Status is used to display the progress of the service request or incident through its stages of opening to closure.
Base Command
bmc-remedy-status-details-get
Input
| Argument Name | Description | Required |
|---|---|---|
| status_name | status_name is the status name whose details the user wants to get. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.Status.Id | String | Status Id. |
| BmcRemedyforce.Status.Name | String | Status name. |
Command Example
!bmc-remedy-status-details-get status_name="OPENED"
Context Example
{
"BmcRemedyforce": {
"Status": {
"Id": "a3w2w000000TfGBAA0",
"Name": "OPENED"
}
}
}
Human Readable Output
Total retrieved status: 1
Id Name a3w2w000000TfGBAA0 OPENED
bmc-remedy-urgency-details-get
This command helps to get urgency details for incidents and service requests. Urgency is used to determine the priority of the incident or service request.
Base Command
bmc-remedy-urgency-details-get
Input
| Argument Name | Description | Required |
|---|---|---|
| urgency_name | urgency_name is the name of the urgency whose details the user wants to get. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.Urgency.Id | String | Urgency Id. |
| BmcRemedyforce.Urgency.Name | String | Urgency name. |
Command Example
!bmc-remedy-urgency-details-get urgency_name="LOW"
Context Example
{
"BmcRemedyforce": {
"Urgency": {
"Id": "a472w000000Tyk6AAC",
"Name": "LOW"
}
}
}
Human Readable Output
Total retrieved urgencies: 1
Id Name a472w000000Tyk6AAC LOW
bmc-remedy-category-details-get
This command helps to get category details for incidents and service requests. Categories allow users to classify the incident or service request using standard classifications to track the reporting purposes.
Base Command
bmc-remedy-category-details-get
Input
| Argument Name | Description | Required |
|---|---|---|
| type | type is the category type whose details the user wants to get. | Optional |
| category_name | category_name is the category name whose details the user wants to get. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.Category.Id | String | Category Id. |
| BmcRemedyforce.Category.Name | String | Category name. |
| BmcRemedyforce.Category.ChildrenCount | String | Number of children of the category. |
Command Example
!bmc-remedy-category-details-get category_name="Email"
Context Example
{
"BmcRemedyforce": {
"Category": {
"ChildrenCount": 0,
"Id": "a212w000000UFfyAAG",
"Name": "Email"
}
}
}
Human Readable Output
Total retrieved categories: 1
Id Name Children Count a212w000000UFfyAAG 0.0
bmc-remedy-queue-details-get
This command helps to get queue details for incidents and service requests. It accepts queue name and type as arguments.
Base Command
bmc-remedy-queue-details-get
Input
| Argument Name | Description | Required |
|---|---|---|
| queue_name | queue_name is the name of the queue as the owner whose details the user wants to get. | Optional |
| type | type will allow the user to filter with its possible values as incident or service request and get details accordingly. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.Queue.Id | String | Queue Id. |
| BmcRemedyforce.Queue.Name | String | Queue name. |
| BmcRemedyforce.Queue.Email | String | Queue email. |
Command Example
!bmc-remedy-queue-details-get queue_name="Application Development"
Context Example
{
"BmcRemedyforce": {
"Queue": {
"Email": "testuser@bmcremedyforce.com",
"Id": "00G2w0000027SeUEAU",
"Name": "Application Development"
}
}
}
Human Readable Output
Total retrieved queue(s): 1
Id Name 00G2w0000027SeUEAU Application Development testuser@bmcremedyforce.com
bmc-remedy-user-details-get
This command helps to get user details for incidents and service requests. It accepts username, account name, email, queue name, and is_staff as arguments.
Base Command
bmc-remedy-user-details-get
Input
| Argument Name | Description | Required |
|---|---|---|
| username | username is the name of the user whose details the user wants to get. | Optional |
| account_name | account_name is the account name of the user whose details the user wants to get. | Optional |
| email is the email address of the user whose details the user wants to get. | Optional | |
| queue_name | queue_name is the group/queue name for which user wants to get user details. | Optional |
| is_staff | is_staff will allow the user to filter with its possible values true or false and get details of users accordingly. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.User.Id | String | User Id which uniquely identifies the user. |
| BmcRemedyforce.User.Name | String | Full name of the user. |
| BmcRemedyforce.User.FirstName | String | First name of the user. |
| BmcRemedyforce.User.LastName | String | Last name of the user. |
| BmcRemedyforce.User.Username | String | Username of the user. |
| BmcRemedyforce.User.Email | String | Email of the user. |
| BmcRemedyforce.User.Phone | String | Phone number of the user. |
| BmcRemedyforce.User.Account | String | Account name of the user. |
| BmcRemedyforce.User.CompanyName | String | Company name of the user. |
| BmcRemedyforce.User.Division | String | Division of the user. |
| BmcRemedyforce.User.Department | String | Department name of the user. |
| BmcRemedyforce.User.Title | String | Title of the user. |
| BmcRemedyforce.User.IsStaff | Boolean | Shows whether the user belongs to the staff or not. |
Command Example
!bmc-remedy-user-details-get queue_name="BMC Client Management"
Context Example
{
"BmcRemedyforce": {
"User": [
{
"CompanyName": "Company",
"Email": "bmcremedyforce@example.com",
"FirstName": "user1",
"Id": "0052w000004nsLdAAI",
"IsStaff": true,
"LastName": "test",
"Name": "user1 test",
"Username": "testuser1@bmcremedyforce.com"
},
{
"CompanyName": "Company",
"Email": "bmcremedyforce@example.com",
"FirstName": "user2",
"Id": "0052w000004nsLaAAI",
"IsStaff": true,
"LastName": "test",
"Name": "user2 test",
"Username": "testuser2@bmcremedyforce.com"
}
]
}
}
Human Readable Output
Total retrieved user(s): 2
Id Username First Name Last Name Company Name Is Staff 0052w000004nsLdAAI testuser1@bmcremedyforce.com user1 test bmcremedy@example.com Company true 0052w000004nsLaAAI testuser2@bmcremedyforce.com user2 test bmcremedy@example.com Company true
bmc-remedy-broadcast-details-get
This command helps to get broadcast details for incidents. Broadcast enables users to send messages to the entire organization, selected groups within the organization and to external customers.
Base Command
bmc-remedy-broadcast-details-get
Input
| Argument Name | Description | Required |
|---|---|---|
| broadcast_name | broadcast_name is the name of the broadcast whose details the user wants to get. | Optional |
| category_name | category_name is the name of the category whose details the user wants to get. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.Broadcast.Id | String | Broadcast Id. |
| BmcRemedyforce.Broadcast.Name | String | Broadcast name. |
| BmcRemedyforce.Broadcast.Description | String | Broadcast description. |
| BmcRemedyforce.Broadcast.Category | String | Broadcast category. |
| BmcRemedyforce.Broadcast.Status | String | Broadcast status. |
| BmcRemedyforce.Broadcast.Priority | String | Broadcast priority. |
| BmcRemedyforce.Broadcast.Urgency | String | Broadcast urgency. |
| BmcRemedyforce.Broadcast.Impact | String | Broadcast impact. |
Command Example
!bmc-remedy-broadcast-details-get broadcast_name="BES Server is down completely"
Context Example
{
"BmcRemedyforce": {
"Broadcast": {
"Category": "Telecommunication",
"Id": "a1v2w000000IaP7AAK",
"Impact": "HIGH",
"Name": "BES Server is down completely",
"Priority": "1",
"Status": "OPENED",
"Urgency": "HIGH"
}
}
}
Human Readable Output
Total retrieved broadcast(s): 1
Id Name Priority Urgency Impact Category Status a1v2w000000IaP7AAK BES Server is down completely 1 HIGH HIGH Telecommunication OPENED
bmc-remedy-incident-get
This command helps to get details of incidents.
Base Command
bmc-remedy-incident-get
Input
| Argument Name | Description | Required |
|---|---|---|
| last_fetch_time | The time range to consider for getting the incident. Use the format “<number> <time unit>”. Example: 12 hours, 7 days, 3 months, 1 year. | Optional |
| incident_number | The unique number of the incident whose details user wants to get. | Optional |
| maximum_incident | The maximum number of the incidents user wants to get. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.Incident.Id | String | Incident Id. |
| BmcRemedyforce.Incident.Number | String | Incident number. |
| BmcRemedyforce.Incident.Priority | String | Incident priority. |
| BmcRemedyforce.Incident.Description | String | Incident description. |
| BmcRemedyforce.Incident.ClientID | String | Incident client Id. |
| BmcRemedyforce.Incident.Status | String | Incident status. |
| BmcRemedyforce.Incident.dueDateTime | String | Incident due datetime. |
| BmcRemedyforce.Incident.Staff | String | Incident staff. |
| BmcRemedyforce.Incident.Queue | String | Incident queue. |
| BmcRemedyforce.Incident.Category | String | Incident category. |
| BmcRemedyforce.Incident.Urgency | String | Incident urgency. |
| BmcRemedyforce.Incident.ClientAccount | String | Incident client account. |
| BmcRemedyforce.Incident.Broadcast | String | Incident broadcast. |
| BmcRemedyforce.Incident.closeDateTime | String | Incident close datetime. |
| BmcRemedyforce.Incident.CreatedDate | String | Incident created date. |
| BmcRemedyforce.Incident.LastModifiedDate | String | Incident last modified date. |
| BmcRemedyforce.Incident.openDateTime | String | Incident open datetime. |
| BmcRemedyforce.Incident.outageTo | String | Incident outage end. |
| BmcRemedyforce.Incident.outageFrom | String | Incident outage start. |
| BmcRemedyforce.Incident.Resolution | String | Incident resolution. |
| BmcRemedyforce.Incident.respondedDateTime | String | Incident responded datetime. |
| BmcRemedyforce.Incident.Service | String | Incident service. |
| BmcRemedyforce.Incident.ServiceOffering | String | Incident service offering. |
| BmcRemedyforce.Incident.Template | String | Incident template. |
| BmcRemedyforce.Incident.Type | String | Incident type. |
| BmcRemedyforce.Incident.Impact | String | Incident impact. |
| BmcRemedyforce.Incident.Asset | String | Incident asset. |
Command Example
!bmc-remedy-incident-get last_fetch_time="1 hours" maximum_incident=2
Context Example
{
"BmcRemedyforce": {
"Incident": {
"Category": "Human Resources",
"ClientID": "Clinet ID",
"CreatedDate": "2020-08-14T12:47:58.000+0000",
"Id": "a2U2w00000096QEEAY",
"Impact": "LOW",
"LastModifiedDate": "2020-08-14T15:18:07.000+0000",
"Number": "00000059",
"Priority": "5",
"Service": "Client Services",
"Staff": "Staff Name",
"Status": "OPENED",
"Template": "Benefits Question - SR",
"Type": "Incident",
"Urgency": "LOW",
"dueDateTime": "2020-08-17T20:47:58.000+0000",
"openDateTime": "2020-08-14T12:47:58.000+0000"
}
}
}
Human Readable Output
Total retrieved incident(s): 1
Number Priority Client ID Status Staff 00000059 5 Client ID OPENED Staff Name
bmc-remedy-service-request-get
This command helps to get the service request details.
Base Command
bmc-remedy-service-request-get
Input
| Argument Name | Description | Required |
|---|---|---|
| service_request_number | The unique number of the service request whose details user wants to get. | Optional |
| last_fetch_time | The time range to consider for getting the service request. Use the format “<number> <time unit>”. Example: 12 hours, 7 days, 3 months, 1 year. | Optional |
| maximum_service_request | The maximum number of the service request user wants to get. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| BmcRemedyforce.ServiceRequest.Id | String | Service Request Id. |
| BmcRemedyforce.ServiceRequest.Number | String | Service Request number. |
| BmcRemedyforce.ServiceRequest.Priority | String | Service Request priority. |
| BmcRemedyforce.ServiceRequest.Description | String | Service Request description. |
| BmcRemedyforce.ServiceRequest.ClientID | String | Service Request client. |
| BmcRemedyforce.ServiceRequest.Status | String | Service Request status. |
| BmcRemedyforce.ServiceRequest.DueDateTime | String | Service Request due datetime. |
| BmcRemedyforce.ServiceRequest.Staff | String | Service Request staff. |
| BmcRemedyforce.ServiceRequest.Queue | String | Service Request queue. |
| BmcRemedyforce.ServiceRequest.Category | String | Service Request category. |
| BmcRemedyforce.ServiceRequest.Urgency | String | Service Request urgency. |
| BmcRemedyforce.ServiceRequest.Broadcast | String | Service Request broadcast. |
| BmcRemedyforce.ServiceRequest.closeDateTime | String | Service Request close datetime. |
| BmcRemedyforce.ServiceRequest.CreatedDate | String | Service Request created date. |
| BmcRemedyforce.ServiceRequest.LastUpdatedDate | String | Service Request last modified date. |
| BmcRemedyforce.ServiceRequest.OpenDateTime | String | Service Request open datetime. |
| BmcRemedyforce.ServiceRequest.Resolution | String | Service Request resolution. |
| BmcRemedyforce.ServiceRequest.ServiceOffering | String | Service Request service offering. |
| BmcRemedyforce.ServiceRequest.Template | String | Service Request template. |
| BmcRemedyforce.ServiceRequest.Impact | String | Service Request impact. |
| BmcRemedyforce.ServiceRequest.BusinessService | String | Service Request business service. |
| BmcRemedyforce.ServiceRequest.ServiceRequestDefinition | String | Service Request definition. |
| BmcRemedyforce.ServiceRequest.ClientAccount | String | Account held by service request. |
| BmcRemedyforce.ServiceRequest.Type | String | Service Desk Type. |
Command Example
!bmc-remedy-service-request-get last_fetch_time="1 hours" maximum_service_request=2
Context Example
{
"BmcRemedyforce": {
"ServiceRequest": {
"BusinessService": "Printing Services",
"Category": "File & Print",
"CreatedDate": "2020-08-14T13:32:17.000+0000",
"DueDateTime": "2020-08-17T21:32:16.000+0000",
"Id": "a2U2w00000096yCEAQ",
"Impact": "LOW",
"LastUpdatedDate": "2020-08-14T14:28:12.000+0000",
"Number": "00000929",
"OpenDateTime": "2020-08-14T13:32:16.000+0000",
"Priority": "5",
"Queue": "Incident Queue",
"ServiceOffering": "Printer Configuration",
"ServiceRequestDefinition": "Setup Printer",
"Status": "OPENED",
"Template": "Printer Setup - SR",
"Type": "Service Request",
"Urgency": "LOW"
}
}
}
Human Readable Output
Total retrieved service request(s): 1
Number Priority Status Queue 00000929 5 OPENED Incident Queue
Configuration parameters
url— BMC Remedyforce URL (e.g. https://example.com) (required)auth_url— BMC Remedyforce Login URLusername— Usernamepassword— Passwordusername_creds— Usernametype— Typecategory— Categoryimpact— Impacturgency— Urgencystatus— Statusqueue— Queuemax_fetch— Max Incidentsfetch_note— Fetch Note(s)query— Query (If provided other filtering parameters will be ignored) e.g. select Name, LastModifiedDate from BMCServiceDesk__Incident__c where BMCServiceDesk__Impact_Id__c = 'High'incidentType— Incident typeincidentFetchInterval— Incidents Fetch Intervalinsecure— Trust any certificate (not secure)proxy— Use system proxy settingsrequest_timeout— HTTP(S) Request Timeout (in seconds) (required)isFetch— Fetch incidentsfirst_fetch— First Fetch Timestamp (<number> <time unit>, e.g., 12 hours, 7 days, 3 months, 1 year)
Commands (19)
-
bmc-remedy-account-details-getReturns account details for incidents and service requests.
-
bmc-remedy-asset-details-getReturns asset or configuration item details for incidents.
-
bmc-remedy-broadcast-details-getReturns broadcast details for incidents, which enables users to send messages to the entire organization, selected groups within the organization and to external customers.
-
bmc-remedy-category-details-getReturns category details for incidents and service requests. Categories allow users to classify the incident or service request using standard classifications to track the reporting purposes.
-
bmc-remedy-impact-details-getReturns the impact details for incidents and service requests. Impact helps calculate the priority of the incident or service request.
-
bmc-remedy-incident-createCreates a new incident.
-
bmc-remedy-incident-getReturns details of incidents.
-
bmc-remedy-incident-updateUpdates details of an incident for a given incident number.
-
bmc-remedy-note-createCreates notes for incidents and service requests.
-
bmc-remedy-queue-details-getReturns queue details for incidents and service requests.
-
bmc-remedy-service-offering-details-getReturns service offering details for incidents.
-
bmc-remedy-service-request-createCreates a new service request, which is the request record generated from the Service Request Definition to manage and track the execution.
-
bmc-remedy-service-request-definition-getReturns details of service request definitions using the Service Request Definition name.
-
bmc-remedy-service-request-getReturns the service request details.
-
bmc-remedy-service-request-updateUpdates details of a service request for a service request number.
-
bmc-remedy-status-details-getReturns status details for incidents and service requests. The status displays the progress of the service request or incident through stages from opening to closure.
-
bmc-remedy-template-details-getReturns template details for incidents. Templates enable users to prepopulate commonly used fields in a form.
-
bmc-remedy-urgency-details-getReturns urgency details for incidents and service requests. Urgency determines the priority of the incident or service request.
-
bmc-remedy-user-details-getReturns user details for incidents and service requests.
import contextlib import traceback from collections.abc import Callable # IMPORTS from datetime import datetime from os import path from typing import Any from xml.etree import ElementTree import dateparser import urllib3 from CommonServerPython import * # noqa: E402 lgtm [py/polluting-import] from requests import Response from requests.exceptions import InvalidSchema, MissingSchema # Disable insecure warnings urllib3.disable_warnings() # CONSTANTS DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ" ALLOWED_DATE_FORMAT_1 = "%Y-%m-%d" # sample - 2020-08-23 ALLOWED_DATE_FORMAT_2 = "%Y-%m-%dT%H:%M:%S.%fZ" # sample - 2020-08-23T08:53:00.000Z ALLOWED_DATE_FORMAT_3 = "%Y-%m-%dT%H:%M:%S.%f%z" # sample - 2020-08-23T08:53:00.000+0530 DISPLAY_DATE_FORMAT = "%B %d, %Y, %I:%M %p" LOGIN_API_VERSION = "35.0" BMC_API_VERSION = "1.0" SALESFORCE_API_VERSION = "v48.0" FIELD_DELIMITER = ";" VALUE_DELIMITER = "=" VALIDATE_JSON = r"(\w+=[^;=]+;( )?)*\w+=[^;=]+" DATE_AND_TIME = "Date & Time [UTC]" HEADER_SECTION_TYPE = "header section" MESSAGES: dict[str, str] = { "TRACEBACK_MESSAGE": "Error when calling {} - ", "BAD_REQUEST_ERROR": "An error occurred while fetching the data. Reason: {}", "AUTHENTICATION_ERROR": "Unauthenticated. Check the configured Username and Password instance parameters.", "AUTHENTICATION_CONFIG_ERROR": "Error authenticating with Remedyforce/Salesforce API. Please check configuration " "parameters.", "FORBIDDEN": "Access to the requested resource is forbidden. Reason: {}", "NOT_FOUND_ERROR": "Requested resource not found. Reason: {}", "INTERNAL_SERVER_ERROR": "Encountered an internal server error with Remedyforce API, unable to complete " "your request. Reason: {}", "REQUEST_TIMEOUT_VALIDATION": "HTTP(S) Request Timeout parameter must be a positive number.", "REQUEST_TIMEOUT_EXCEED_ERROR": "Value is too large for HTTP(S) Request Timeout parameter. Maximum value allowed " "is 120 seconds.", "MISSING_SCHEMA_ERROR": "Invalid API URL. No schema supplied: http(s).", "INVALID_SCHEMA_ERROR": "Invalid API URL. Supplied schema is invalid, supports http(s).", "CONNECTION_ERROR": "Connectivity failed. Check your internet connection, the API URL or try increasing the HTTP(s)" " Request Timeout.", "PROXY_ERROR": "Proxy error - cannot connect to proxy. Either try clearing the 'Use system proxy' checkbox or " "check the host, authentication details and connection details for the proxy.", "DATA_PARSING_ERROR": "Failed parsing response data.", "GET_OUTPUT_MESSAGE": "Total retrieved {0}: {1}", "FAILED_MESSAGE": "The request to {0} {1} has failed.", "INVALID_MAX_INCIDENT_ERROR": "Parameter Max Incidents must be positive integer.", "MULTIPLE_WHERE_CLAUSE_ERROR": 'Multiple "where" clauses are not allowed inside query parameter.', "PARAMETER_TYPE_EMPTY_ERROR": "Parameter Type is mandatory.", "SERVICE_REQ_DEF_NOT_FOUND": 'Can not find a service request definition "{}".', "INVALID_FIELDS_ERROR": "Fields '{}' are not allowed to pass in '{}' argument.", "INVALID_FORMAT_ERROR": "Invalid data format of {} argument, enter format like - '{}'.", "UNEXPECTED_ERROR": "An unexpected error occurred.", "CREATE_SERVICE_REQUEST_WARNING": "The service request {} is created but failed to set following fields " "'{}' into service request due to reason: {}.", "UPDATE_SERVICE_REQUEST_WARNING": "The service request {} is updated but failed to update following fields: {} " "into service request due to reason: {}.", "NOT_FOUND_SERVICE_REQUEST": "Can not find service request {}.", "NOT_FOUND_INCIDENT": "Can not find incident {}.", "INVALID_DATA_FORMAT": "Invalid data format of {} argument.", "NOTE_CREATE_FAIL": "Can not find Service request/Incident {}.", "INVALID_ENTITY_NAME": 'No records found for {} "{}".', "NO_ENTITY_FOUND": "No {} found on configured instance of BMC Helix Remedyforce.", "INVALID_ADDITIONAL_ARGUMENT": "Additional arguments field contains following the default argument(s) for this " "command: {}.", "INVALID_FETCH_INCIDENT_QUERY_ERROR": "The provided query is invalid.", "INVALID_TYPE_FOR_CATEGORIES": "The given value for {} is invalid. Valid {}: {}.", "EMPTY_SERVICE_REQUEST": "'service_request_number' can not be empty.", "EMPTY_REQUIRED_ARGUMENT": "'{}' can not be empty.", "DATE_PARSE_ERROR": "Cannot parse datetime for field - {}. Expected format is yyyy-MM-ddTHH:mm:ss.SSS+/-HHmm or " "yyyy-MM-ddTHH:mm:ss.SSSZ.", "DATE_VALIDATION_ERROR": "{} must be later than the {}.", "MAX_INCIDENT_LIMIT": "Values allowed for {} is 1 to 500.", } POSSIBLE_CATEGORY_TYPES = ["All", "Service Request", "Incident"] HR_MESSAGES: dict[str, str] = { "NOTE_CREATE_SUCCESS": "The service request/incident {} is successfully updated with the note.", "GET_COMMAND_DETAILS_SUCCESS": "Total retrieved {}: {}", "SERVICE_REQUEST_CREATE_SUCCESS": "The service request {} is successfully created.", "SERVICE_REQUEST_UPDATE_SUCCESS": "The service request {} is successfully updated.", "CREATE_INCIDENT_SUCCESS": "The incident {} is successfully created.", "CREATE_INCIDENT_WARNING": "The incident {} is created but failed to set following fields " "'{}' into incident due to reason: {}", "CREATE_INCIDENT_FAILURE": "The request to create the incident failed due to the following reason: {}", "UPDATE_INCIDENT_SUCCESS": "The incident {} is successfully updated.", "COMMAND_FAILURE": "Failed to execute {} command. Error: {}", "UPDATE_INCIDENT_FAILURE": "Error: The request to update incident failed due to the following reason: {}.", "NO_QUEUE_FOUND": "No queue details found for the given argument(s).", "NO_USERS_FOUND": "No user(s) found for the given argument(s).", "NO_ASSETS_FOUND": "No asset(s) found for the given argument(s).", "NO_BROADCAST_DETAILS_FOUND": "No broadcast details found for the given argument(s).", "NOT_FOUND_FOR_ARGUMENTS": "No {} found for the given argument(s).", "NOT_FOUND_SERVICE_REQUEST_DEF": 'No records found for service_request_definition_name "{}".', "NO_INCIDENT_DETAILS_FOUND": "No incident(s) for the given argument(s).", "NO_SERVICE_REQUEST_DETAILS_FOUND": "No service request(s) found for the given argument(s).", } URL_SUFFIX: dict[str, str] = { "TEST_MODULE": f"/services/apexrest/BMCServiceDesk/{BMC_API_VERSION}/ServiceUtil/UserDetail", "GET_SERVICE_REQUEST_DEFINITION": f"/services/apexrest/BMCServiceDesk/{BMC_API_VERSION}/ServiceRequestDefinition", "CREATE_NOTE_COMMAND": "/services/apexrest/BMCServiceDesk/{}/ServiceRequest/{}/clientnote", "SALESFORCE_QUERY": f"/services/data/{SALESFORCE_API_VERSION}/query", "FETCH_SRD": f"/services/apexrest/BMCServiceDesk/{BMC_API_VERSION}/ServiceRequestDefinition", "UPDATE_INCIDENT": f"services/data/{SALESFORCE_API_VERSION}/sobjects/BMCServiceDesk__Incident__c", "SERVICE_REQUEST": f"services/apexrest/BMCServiceDesk/{BMC_API_VERSION}/ServiceRequest", "CREATE_INCIDENT": f"/services/apexrest/BMCServiceDesk/{BMC_API_VERSION}/Incident", "DOWNLOAD_ATTACHMENT": "/sfc/servlet.shepherd/document/download/{}?operationContext=S1", } PRIORITY_TO_SEVERITY_MAP = {"5": 0, "4": 1, "3": 2, "2": 3, "1": 4} OUTPUT_PREFIX: dict[str, str] = { "SERVICE_REQUEST": "BmcRemedyforce.ServiceRequest", # Using this in return_warning for setting context. "SERVICE_REQUEST_WARNING": "BmcRemedyforce.ServiceRequest(val.Number === obj.Number)", "NOTE": "BmcRemedyforce.Note", "SERVICE_REQUEST_DEFINITION": "BmcRemedyforce.ServiceRequestDefinition", "TEMPLATE": "BmcRemedyforce.Template", "USER": "BmcRemedyforce.User", "SERVICE_OFFERING": "BmcRemedyforce.ServiceOffering", "IMPACT": "BmcRemedyforce.Impact", "INCIDENT": "BmcRemedyforce.Incident", # Using this in return_warning for setting context. "INCIDENT_WARNING": "BmcRemedyforce.Incident(val.Id === obj.Id)", "ASSET": "BmcRemedyforce.Asset", "ACCOUNT": "BmcRemedyforce.Account", "STATUS": "BmcRemedyforce.Status", "URGENCY": "BmcRemedyforce.Urgency", "CATEGORY": "BmcRemedyforce.Category", "QUEUE": "BmcRemedyforce.Queue", "BROADCAST": "BmcRemedyforce.Broadcast", } SALESFORCE_QUERIES: dict[str, str] = { "SERVICE_REQUEST_DEF_NAME": "select id,name from BMCServiceDesk__SRM_RequestDefinition__c where name='{}'", "GET_ID_FROM_NAME": "select id from BMCServiceDesk__Incident__c where name='{}'", "GET_TEMPLATE_DETAILS": "select id,name,BMCServiceDesk__description__c," "BMCServiceDesk__HasRecurrence__c from BMCServiceDesk__SYSTemplate__c " "where IsDeleted=false and BMCServiceDesk__inactive__c = false " "and BMCServiceDesk__systemTemplate__c = false " "and BMCServiceDesk__templateFor__c = 'Incident' ", "GET_USER_DETAILS": "select id,name, firstname, lastname, username, email, phone, companyname, " "division, department, title, BMCServiceDesk__IsStaffUser__c, BMCServiceDesk__Account_Name__c " "from user where isactive=true and BMCServiceDesk__User_License__c != null", "GET_USER_DETAILS_USING_QUEUE": "id IN (SELECT userOrGroupId FROM groupmember WHERE group.name ='{}')", "GET_ID_FROM_SERVICE_REQUEST_NUMBER": "select id, name, BMCServiceDesk__isServiceRequest__c from " "BMCServiceDesk__Incident__c where name = '{}'", "GET_IMPACTS": "select id,name from BMCServiceDesk__Impact__c where IsDeleted=false and " "BMCServiceDesk__inactive__c = false", "FETCH_INCIDENT_QUERY": "select lastmodifieddate,BMCServiceDesk__FKOpenBy__r.name," " BMCServiceDesk__outageto__c,BMCServiceDesk__outagefrom__c," "BMCServiceDesk__FKbmc_baseelement__r.name,BMCServiceDesk__FKserviceoffering__r.name," "BMCServiceDesk__FKBusinessservice__r.name,BMCServiceDesk__closedatetime__c," "BMCServiceDesk__opendatetime__c, BMCServiceDesk__respondeddatetime__c," " BMCServiceDesk__FKBroadcast__r.name,BMCServiceDesk__incidentResolution__c," " BMCServiceDesk__FKRequestDefinition__r.name," "BMCServiceDesk__FKTemplate__r.name,LastModifiedById," "BMCServiceDesk__FKTemplate__c,id,BMCServiceDesk__Priority_ID__c," "BMCServiceDesk__Type__c,name, CreatedDate," " BMCServiceDesk__incidentDescription__c," "BMCServiceDesk__Category_ID__c, BMCServiceDesk__Impact_Id__c," " BMCServiceDesk__Urgency_ID__c, BMCServiceDesk__Status_ID__c," " BMCServiceDesk__dueDateTime__c, BMCServiceDesk__queueName__c," " BMCServiceDesk__Client_Account__c, BMCServiceDesk__Client_Name__c," " BMCServiceDesk__isServiceRequest__c from BMCServiceDesk__Incident__c " "where {0} IsDeleted=false and BMCServiceDesk__inactive__c = false " "and BMCServiceDesk__isServiceRequest__c = {1} and" " BMCServiceDesk__ServiceRequest__c = '{2}' " "and LastModifiedDate > {3} ORDER BY LastModifiedDate ASC NULLS LAST LIMIT {4}", "GET_SERVICE_OFFERING_DETAILS": "select id,name from BMCServiceDesk__BMC_BaseElement__c " "where BMCServiceDesk__ServiceType__c = 'Offering' " "and IsDeleted=false and BMCServiceDesk__inactive__c = false ", "GET_ASSET_DETAILS": "select id,name,BMCServiceDesk__Description__c,BMCServiceDesk__ClassName__c," "BMCServiceDesk__CITag__c,BMCServiceDesk__InstanceType__c " "from BMCServiceDesk__BMC_BaseElement__c " "where IsDeleted=false and BMCServiceDesk__inactive__c = false ", "GET_URGENCY_DETAILS": "select id,name from BMCServiceDesk__Urgency__c where IsDeleted=false " "and BMCServiceDesk__inactive__c = false ", "FILTER_ASSET_CLASSES": " and (BMCServiceDesk__InstanceType__c='Asset' or BMCServiceDesk__InstanceType__c='CI / Asset')", "FILTER_CI_CLASSES": " and (BMCServiceDesk__InstanceType__c='CI' or BMCServiceDesk__InstanceType__c='CI / Asset')", "FILTER_WITH_NAME": " and name ='{}'", "ORDER_BY_NAME": " ORDER by name", "GET_ACCOUNT_DETAILS": "select id,name from Account where BMCServiceDesk__inactive__c=false " "and BMCServiceDesk__Remedyforce_Account__c = true ", "GET_STATUS": "select id,name from BMCServiceDesk__Status__c where BMCServiceDesk__inactive__c=false and " "BMCServiceDesk__appliesToIncident__c=true", "GET_CATEGORIES": "select id,name, BMCServiceDesk__children__c from BMCServiceDesk__Category__c " "where BMCServiceDesk__inactive__c = false", "GET_QUEUE_DETAIL": "select id, name, email from group where type='queue' {}", "GET_QUEUE_DETAIL_FOR_SPECIFIC_TYPE": "SELECT QueueId, Queue.Name, Queue.email FROM " " QueueSobject WHERE SobjectType = '{}'", "GET_BROADCAST_DETAILS": "select id,name,BMCServiceDesk__Priority_ID__c,BMCServiceDesk__Urgency_ID__c," "BMCServiceDesk__Impact_ID__c,BMCServiceDesk__broadcastDescription__c," "BMCServiceDesk__Category_ID__c,BMCServiceDesk__Status_ID__c" " from BMCServiceDesk__Broadcasts__c where BMCServiceDesk__inactive__c=false", "QUERY_AND": " and ", "GET_ATTACHMENTS": "select Id, ContentDocumentId, ContentDocument.Title, ContentDocument.Description, " "ContentDocument.CreatedDate, ContentDocument.CreatedBy.Name from ContentDocumentLink " "where LinkedEntityId = '{}'", "GET_NOTES": "select BMCServiceDesk__note__c, CreatedBy.Name, CreatedDate, " "BMCServiceDesk__incidentId__c,Name,BMCServiceDesk__actionId__c, " "BMCServiceDesk__description__c from BMCServiceDesk__IncidentHistory__c " "where BMCServiceDesk__incidentId__c='{}' and IsDeleted=false", "GET_INCIDENTS": "select lastmodifieddate,BMCServiceDesk__FKOpenBy__r.name," " BMCServiceDesk__outageto__c,BMCServiceDesk__outagefrom__c," "BMCServiceDesk__FKbmc_baseelement__r.name,BMCServiceDesk__FKserviceoffering__r.name," "BMCServiceDesk__FKBusinessservice__r.name,BMCServiceDesk__closedatetime__c," "BMCServiceDesk__opendatetime__c, BMCServiceDesk__respondeddatetime__c," " BMCServiceDesk__FKBroadcast__r.name,BMCServiceDesk__incidentResolution__c," " BMCServiceDesk__FKRequestDefinition__r.name," "BMCServiceDesk__FKTemplate__r.name," "id,BMCServiceDesk__Priority_ID__c," "BMCServiceDesk__Type__c,name, CreatedDate," " BMCServiceDesk__incidentDescription__c," "BMCServiceDesk__Category_ID__c, BMCServiceDesk__Impact_Id__c," " BMCServiceDesk__Urgency_ID__c, BMCServiceDesk__Status_ID__c," " BMCServiceDesk__dueDateTime__c, BMCServiceDesk__queueName__c," " BMCServiceDesk__Client_Account__c, BMCServiceDesk__Client_Name__c" " from BMCServiceDesk__Incident__c " "where {} IsDeleted=false and BMCServiceDesk__inactive__c = false " "and BMCServiceDesk__isServiceRequest__c = {} and" " BMCServiceDesk__ServiceRequest__c = '{}' ORDER BY LastModifiedDate DESC NULLS LAST ", "GET_SERVICE_REQUEST": "select lastmodifieddate,BMCServiceDesk__FKOpenBy__r.name," " BMCServiceDesk__outageto__c,BMCServiceDesk__outagefrom__c," "BMCServiceDesk__FKbmc_baseelement__r.name,BMCServiceDesk__FKserviceoffering__r.name," "BMCServiceDesk__FKBusinessservice__r.name,BMCServiceDesk__closedatetime__c," "BMCServiceDesk__opendatetime__c, BMCServiceDesk__respondeddatetime__c," " BMCServiceDesk__FKBroadcast__r.name,BMCServiceDesk__incidentResolution__c," " BMCServiceDesk__FKRequestDefinition__r.name," "BMCServiceDesk__FKTemplate__r.name," "id,BMCServiceDesk__Priority_ID__c," "BMCServiceDesk__Type__c,name, CreatedDate," " BMCServiceDesk__incidentDescription__c," "BMCServiceDesk__Category_ID__c, BMCServiceDesk__Impact_Id__c," " BMCServiceDesk__Urgency_ID__c, BMCServiceDesk__Status_ID__c," " BMCServiceDesk__dueDateTime__c, BMCServiceDesk__queueName__c," " BMCServiceDesk__Client_Account__c, BMCServiceDesk__Client_Name__c" " from BMCServiceDesk__Incident__c " "where {} IsDeleted=false and BMCServiceDesk__inactive__c = false " "and BMCServiceDesk__isServiceRequest__c = {} and" " BMCServiceDesk__ServiceRequest__c = '{}' ORDER BY LastModifiedDate DESC NULLS LAST ", } # in seconds REQUEST_TIMEOUT_MAX_VALUE = 120 AVAILABLE_FIELD_LIST = ["category_id", "queue_id", "staff_id", "status_id", "urgency_id", "client_id", "impact_id"] DEFAULT_INCIDENT_ARGUMENTS = [ "client_id", "description", "open_datetime", "due_datetime", "queue_id", "template_id", "category_id", "urgency_id", "status_id", "staff_id", "impact_id", ] ALL_INSTANCE_TYPE: dict[str, str] = {"all_classes": "All Classes", "asset_classes": "Asset Classes", "ci_classes": "CI Classes"} SERVICE_REQUEST_CATEGORY_OBJECT = "BMCServiceDesk__AvailableForServiceCatalog__c" INCIDENT_CATEGORY_OBJECT = "BMCServiceDesk__AvailableForIncidents__c" MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS: dict[str, str] = { "client_id": "BMCServiceDesk__FKClient__c", "template_id": "BMCServiceDesk__FKTemplate__c", "service_request_definition_id": "BMCServiceDesk__FKRequestDefinition__c", "category_id": "BMCServiceDesk__FKCategory__c", "broadcast_id": "BMCServiceDesk__FKBroadcast__c", "description": "BMCServiceDesk__incidentDescription__c", "resolution_id": "BMCServiceDesk__incidentResolution__c", "impact_id": "BMCServiceDesk__FKImpact__c", "urgency_id": "BMCServiceDesk__FKUrgency__c", "priority_id": "BMCServiceDesk__FKPriority__c", "status_id": "BMCServiceDesk__FKStatus__c", "opened_date": "BMCServiceDesk__openDateTime__c", "responded_date": "BMCServiceDesk__respondedDateTime__c", "due_date": "BMCServiceDesk__dueDateTime__c", "closed_date": "BMCServiceDesk__closeDateTime__c", "service_id": "BMCServiceDesk__FKBusinessService__c", "service_offering_id": "BMCServiceDesk__FKServiceOffering__c", "asset_id": "BMCServiceDesk__FKBMC_BaseElement__c", "outage_start": "BMCServiceDesk__outageFrom__c", "outage_end": "BMCServiceDesk__outageTo__c", "queue_id": "OwnerId", "staff_id": "BMCServiceDesk__FKOpenBy__c", "account_id": "BMCServiceDesk__FKAccount__c", "account_name": "BMCServiceDesk__Account_Name__c", "is_staff": "BMCServiceDesk__IsStaffUser__c", "category": "BMCServiceDesk__Category_ID__c", "impact": "BMCServiceDesk__Impact_Id__c", "urgency": "BMCServiceDesk__Urgency_ID__c", "status": "BMCServiceDesk__Status_ID__c", "queue": "BMCServiceDesk__queueName__c", "description_object": "BMCServiceDesk__description__c", "asset_description_object": "BMCServiceDesk__Description__c", "has_recurrence": "BMCServiceDesk__HasRecurrence__c", "ci_tag": "BMCServiceDesk__CITag__c", "class_name_object": "BMCServiceDesk__ClassName__c", "instance_type_object": "BMCServiceDesk__InstanceType__c", "incident_priority": "BMCServiceDesk__Priority_ID__c", "incident_client_name": "BMCServiceDesk__Client_Name__c", } FIELD_MAPPING_FOR_GET_INCIDENTS = { "LastModifiedDate": "LastUpdatedDate", "BMCServiceDesk__FKOpenBy__r": "Staff", "BMCServiceDesk__FKServiceOffering__r": "ServiceOffering", "BMCServiceDesk__FKBusinessService__r": "BusinessService", "BMCServiceDesk__closeDateTime__c": "closeDateTime", "BMCServiceDesk__openDateTime__c": "OpenDateTime", "BMCServiceDesk__FKBroadcast__r": "Broadcast", "BMCServiceDesk__incidentResolution__c": "Resolution", "BMCServiceDesk__FKRequestDefinition__r": "ServiceRequestDefinition", "BMCServiceDesk__FKTemplate__r": "Template", "LastModifiedById": "LastModifiedBy", "Id": "Id", "BMCServiceDesk__Priority_ID__c": "Priority", "BMCServiceDesk__Type__c": "Type", "Name": "Number", "CreatedDate": "CreatedDate", "BMCServiceDesk__incidentDescription__c": "Description", "BMCServiceDesk__Category_ID__c": "Category", "BMCServiceDesk__Impact_Id__c": "Impact", "BMCServiceDesk__Urgency_ID__c": "Urgency", "BMCServiceDesk__Status_ID__c": "Status", "BMCServiceDesk__dueDateTime__c": "DueDateTime", "BMCServiceDesk__queueName__c": "Queue", "BMCServiceDesk__Client_Account__c": "ClientAccount", "BMCServiceDesk__Client_Name__c": "ClientID", } INCIDENT_PREFIX = {"Incident": "IN", "Service Request": "SR"} QUEUE_TYPES = {"Incident/Service Request": "BMCServiceDesk__Incident__c"} SOAP_LOGIN_URL = "" class Client(BaseClient): """ Client will implement the service API, and should not contain any Demisto logic. Should only do requests and return data. """ def __init__(self, username: str, password: str, request_timeout: int, *args, **kwargs): """ BMCRemedyForceClient implements logic to authenticate each http request with bearer token :param soap_login_url: Salesforce soap login url, with default value """ super().__init__(*args, **kwargs) self._username = username self._password = password self._request_timeout = request_timeout self.proxies = handle_proxy() # Throws a ValueError if Proxy is empty in configuration. if kwargs.get("proxy", False) and not self.proxies.get("https", ""): raise ValueError(MESSAGES["PROXY_ERROR"]) def http_request(self, method, url_suffix, headers=None, json_data=None, params=None): """ Overrides Base client's _http_request function to authenticate each request with Bearer authorization token containing valid session id which is cached in integration context :type method: ``str`` :param method: The HTTP method, for example: GET, POST, and so on. :type url_suffix: ``str`` :param url_suffix: The API endpoint. :type headers: ``dict`` :param headers: Headers to send in the request. If None, will use self._headers. :type json_data: ``dict`` :param json_data: The dictionary to send in a 'POST' request. :type params: ``dict`` :param params: URL parameters to specify the query. :return: http response or json content of the response :rtype: ``dict`` or ``requests.Response`` :raises ConnectionError: If there is proxy error or connection error while making the http call. :raises DemistoException: If there is any other issues while making the http call. """ session_id = self.get_session_id() default_headers = {"Authorization": f"Bearer {session_id}"} if headers: default_headers.update(headers) # Passing "response" in resp_type, to ensure we always get the full response object and deal with # the response type here # Passing specific ok_codes from here, to keep the control of dealing with ok codes from this wrapper method with http_exception_handler(): resp = self._http_request( method=method, url_suffix=url_suffix, headers=default_headers, json_data=json_data, params=params, timeout=self._request_timeout, resp_type="response", ok_codes=(200, 201, 202, 204, 400, 401, 403, 404, 500), proxies=self.proxies, ) if resp.ok: if resp.status_code == 204: # Handle empty response return resp else: return resp.json() else: handle_error_response(resp) # noqa: RET503 def get_session_id(self): """ Get session id from Demisto integration context. If not found in integration context or expired, generate a new session, set integration context and return session id :return: a valid session id to be used as a bearer token to access remedyforce and salesforce api """ integration_context = demisto.getIntegrationContext() session_id = integration_context.get("sessionId") valid_until = integration_context.get("validUntil") # Return session id from integration context, if found and not expired if session_id and valid_until and time.time() < valid_until: return session_id # Generate session and set integration context resp = self.get_salesforce_session() if resp.status_code == 200: resp_root = ElementTree.fromstring(resp.content) for session_id in resp_root.iter("{urn:partner.soap.sforce.com}sessionId"): integration_context["sessionId"] = session_id.text for session_seconds_valid in resp_root.iter("{urn:partner.soap.sforce.com}sessionSecondsValid"): shorten_by = 5 # Shorten token validity period by 5 seconds for safety if session_seconds_valid.text: integration_context["validUntil"] = time.time() + (float(session_seconds_valid.text) - shorten_by) demisto.setIntegrationContext(integration_context) return integration_context["sessionId"] else: demisto.debug(f"RemedyForce Login server returned: {resp.text}") raise DemistoException(MESSAGES["AUTHENTICATION_CONFIG_ERROR"]) def get_salesforce_session(self): """ Get salesforce soap login response from soap_login_url for the auth credentials provided in instance parameters :return: Xml response from login SOAP call :raises ConnectionError: If there is proxy error or connection error while making the http call. :raises DemistoException: If there is any other issues while making the http call. """ headers = {"Content-Type": "text/xml", "SOAPAction": "Login"} request_payload = f"""<env:Envelope xmlns:xsd=" http://www.w3.org/2001/XMLSchema " xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance " xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"> <env:Body> <n1:login xmlns:n1="urn:partner.soap.sforce.com"> <n1:username>{self._username}</n1:username> <n1:password>{self._password}</n1:password> </n1:login> </env:Body> </env:Envelope>""" with http_exception_handler(): return self._http_request( "POST", "", full_url=SOAP_LOGIN_URL, data=request_payload, headers=headers, timeout=self._request_timeout, ok_codes=(200, 201, 202, 204, 400, 401, 404, 500), resp_type="response", ) """ HELPER FUNCTIONS """ @contextlib.contextmanager def http_exception_handler(): """ Exception handler for handling different exceptions while making http calls. :return: None :raises ConnectionError: If there is proxy error or connection error while making the http call. :raises DemistoException: If there is any other issues while making the http call. """ try: yield except MissingSchema: raise DemistoException(MESSAGES["MISSING_SCHEMA_ERROR"]) except InvalidSchema: raise DemistoException(MESSAGES["INVALID_SCHEMA_ERROR"]) except DemistoException as e: if "Proxy Error" in str(e): raise ConnectionError(MESSAGES["PROXY_ERROR"]) elif "ConnectionError" in str(e) or "ConnectTimeout" in str(e): raise ConnectionError(MESSAGES["CONNECTION_ERROR"]) else: raise e def handle_error_response(response: Response) -> None: """ Handles http error response and raises DemistoException with appropriate message. :param response: Http response :return: None :raises DemistoException: With proper error message for different error scenarios """ if response.status_code == 401: # Invalidate session from integration context. integration_context = demisto.getIntegrationContext() integration_context["sessionId"] = None integration_context["validUntil"] = 0 demisto.setIntegrationContext(integration_context) error_message = "" try: if isinstance(response.json(), dict): error_message = response.json().get("message", MESSAGES["UNEXPECTED_ERROR"]) elif isinstance(response.json(), list) and isinstance(response.json()[0], dict): error_message = response.json()[0].get("message", MESSAGES["UNEXPECTED_ERROR"]) except ValueError: # ignoring json parsing errors pass status_code_messages = { 400: MESSAGES["BAD_REQUEST_ERROR"].format(error_message), 401: MESSAGES["AUTHENTICATION_ERROR"], 404: MESSAGES["NOT_FOUND_ERROR"].format(error_message), 403: MESSAGES["FORBIDDEN"].format(error_message), 500: MESSAGES["INTERNAL_SERVER_ERROR"].format(error_message), } if response.status_code in status_code_messages: LOG(f"Response Code: {response.status_code}, Reason: {status_code_messages[response.status_code]}") raise DemistoException(status_code_messages[response.status_code]) else: response.raise_for_status() def is_service_request_number_blank(service_request_number: str) -> str: """ Check if service_request_number is empty or None then raise the exception. :param service_request_number: service_request_number :type service_request_number: ``str`` :return: service_request_number :rtype: ``str`` :raises ValueError: if service_request_number is empty or None. """ if service_request_number: service_request_number = remove_prefix("sr", service_request_number) return service_request_number else: raise ValueError(MESSAGES["EMPTY_SERVICE_REQUEST"]) def is_parameter_blank(parameter: str, parameter_name: str) -> str: """ Check if parameter is empty or None then raise the exception. :param parameter: Parameter :type parameter: ``str`` :param parameter_name: Name of the parameter in string :type parameter_name: ``str`` :return: parameter :rtype: ``str`` :raises ValueError: if parameter is empty or None. """ if not parameter: raise ValueError(MESSAGES["EMPTY_REQUIRED_ARGUMENT"].format(parameter_name)) else: return parameter def get_request_timeout(): """ Validate and return the request timeout parameter. The parameter must be a positive integer. Default value is set to 60 seconds for API request timeout. :return: request_timeout: Request timeout value. :raises ValueError: if timeout parameter is not a positive integer or exceeds the maximum allowed value """ try: request_timeout = int(demisto.params().get("request_timeout") or 60) except ValueError: raise ValueError(MESSAGES["REQUEST_TIMEOUT_VALIDATION"]) if request_timeout <= 0: raise ValueError(MESSAGES["REQUEST_TIMEOUT_VALIDATION"]) elif request_timeout > REQUEST_TIMEOUT_MAX_VALUE: raise ValueError(MESSAGES["REQUEST_TIMEOUT_EXCEED_ERROR"]) return request_timeout def validate_max_incidents(max_incidents: str) -> None: """ Validates the value of max_incident parameter. :params max_incidents: In fetch-incident maximum number of incidents to return. :raises ValueError: if max incidents parameter is not a positive integer. :return: None """ try: max_incidents_int = int(max_incidents) if max_incidents_int <= 0: raise ValueError except ValueError: raise ValueError(MESSAGES["INVALID_MAX_INCIDENT_ERROR"]) def prepare_query_for_fetch_incidents(params: dict[str, str], start_time: int) -> str: """ Prepares a query for fetch-incidents. :param params: Dictionary contains parameters. :param start_time: Timestamp to start fetch after. :raises ValueError: if query is none as well as type parameter is none. :return: string query. """ start_time = timestamp_to_datestring(start_time, is_utc=True) if params.get("query", ""): # If query parameter is provided. query = params["query"].lower() where_count = query.count("where") if where_count > 1: raise ValueError(MESSAGES["MULTIPLE_WHERE_CLAUSE_ERROR"]) elif where_count == 0: if query.count("from"): from_search_end = re.search(pattern="from \\w+", string=query).end() # type: ignore return query[:from_search_end] + f" where LastModifiedDate > {start_time}" + query[from_search_end:] raise ValueError(MESSAGES["INVALID_FETCH_INCIDENT_QUERY_ERROR"]) where_search_end = re.search(pattern="where", string=query).end() # type: ignore return query[:where_search_end] + f" LastModifiedDate > {start_time} and" + query[where_search_end:] max_incidents = params.get("max_fetch", "10") validate_max_incidents(max_incidents) if not params.get("type", ""): raise ValueError(MESSAGES["PARAMETER_TYPE_EMPTY_ERROR"]) fetch_type = ("false", "No") if params["type"] == "BMC Remedyforce Incident" else ("true", "Yes") fields = "" for param_key, param_val in params.items(): if param_key in ["category", "impact", "urgency", "status", "queue"] and param_val: fields += f"{MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS[param_key]}='{param_val}'" fields += SALESFORCE_QUERIES["QUERY_AND"] return SALESFORCE_QUERIES["FETCH_INCIDENT_QUERY"].format(fields, *fetch_type, start_time, max_incidents) def prepare_iso_date_string(date_string: str) -> str: """ Prepares iso date string from date string. :param date_string: String representing date. :return: string representing date in iso format. """ if date_string: parsed_date = dateparser.parse(date_string) return parsed_date.isoformat() if parsed_date else "" return "" def prepare_date_or_markdown_fields_for_fetch_incidents(fields: dict[str, Any]) -> None: """ Prepares the date and markdown fields for incident or service request. :param fields: fields received in response of incident or service requests. :returns: None """ fields["BMCServiceDesk__closeDateTime__c"] = prepare_iso_date_string(fields.get("BMCServiceDesk__closeDateTime__c", "")) fields["BMCServiceDesk__dueDateTime__c"] = prepare_iso_date_string(fields.get("BMCServiceDesk__dueDateTime__c", "")) fields["CreatedDate"] = prepare_iso_date_string(fields.get("CreatedDate", "")) fields["BmcLastModifiedDate"] = prepare_iso_date_string(fields.get("LastModifiedDate", "")) fields["BMCServiceDesk__openDateTime__c"] = prepare_iso_date_string(fields.get("BMCServiceDesk__openDateTime__c", "")) fields["BMCServiceDesk__outageFrom__c"] = prepare_iso_date_string(fields.get("BMCServiceDesk__outageFrom__c", "")) fields["BMCServiceDesk__outageTo__c"] = prepare_iso_date_string(fields.get("BMCServiceDesk__outageTo__c", "")) fields["BMCServiceDesk__respondedDateTime__c"] = prepare_iso_date_string( fields.get("BMCServiceDesk__respondedDateTime__c", "") ) fields["Attachments"] = tableToMarkdown( "", fields.get("attachments", []), headers=["File", "Download Link", DATE_AND_TIME, "Created By"] ) fields["Notes"] = tableToMarkdown( "", fields.get("notes", []), ["Incident History ID", "Action~", DATE_AND_TIME, "Sender", "Description", "Note"] ) fields["ServiceRequest"] = tableToMarkdown("", fields.get("service_request_details", {})) remove_nulls_from_dictionary(fields) def validate_params_for_fetch_incidents(params: dict[str, Any]) -> None: """ Validates parameters for fetch-incidents command. :param params: parameters dictionary. """ if params.get("isFetch", False): query = params.get("query", "") if query: from_count = query.count("from") if from_count < 1: raise ValueError(MESSAGES["INVALID_FETCH_INCIDENT_QUERY_ERROR"]) where_count = query.count("where") if where_count > 1: raise ValueError(MESSAGES["MULTIPLE_WHERE_CLAUSE_ERROR"]) else: validate_max_incidents(params.get("max_fetch", 10)) if not params.get("type", ""): raise ValueError(MESSAGES["PARAMETER_TYPE_EMPTY_ERROR"]) def prepare_incident_for_fetch_incidents(record: dict[str, Any], params: dict[str, Any]) -> dict[str, Any]: """ Prepares incident dictionary as per demisto standard. :param record: Dictionary containing information of incident. :param params: Demisto parameters. :return: Dictionary containing information related to incident. """ record = remove_empty_elements(record) name = record.get("Name", "") if record.get("BMCServiceDesk__Type__c", ""): name = f"{INCIDENT_PREFIX.get(record['BMCServiceDesk__Type__c'], '')}{record.get('Name', '')}" prepare_date_or_markdown_fields_for_fetch_incidents(record) # Setting severity from priority record["Bmc Severity"] = PRIORITY_TO_SEVERITY_MAP.get(record.get("BMCServiceDesk__Priority_ID__c", 0), 0) incident = {"name": name, "rawJSON": json.dumps(record), "details": json.dumps(record) if params.get("query", "") else ""} remove_nulls_from_dictionary(incident) return incident def prepare_outputs_for_categories( records: list[dict[str, Any]], ) -> tuple[list[dict[str, Any | None]], list[dict[str, Any | None]]]: """ Prepares human readables and context output for 'bmc-remedy-category-details-get' command. :param records: List containing records of categories from rest API. :return: Tuple containing human-readable and context-ouputs. """ outputs = [] hr_output = [] for each_record in records: temp = {} temp1 = {} temp["Id"] = temp1["Id"] = each_record.get("Id") temp["Name"] = temp1["Name"] = each_record.get("Name") temp["Children Count"] = temp1["ChildrenCount"] = each_record.get("BMCServiceDesk__children__c") hr_output.append(temp) outputs.append(temp1) return hr_output, outputs def prepare_broadcast_details_get_output(broadcast_records: list[dict[str, Any]]) -> list[dict[str, Any]]: """ Prepares context output for broadcast_details_get command. :param broadcast_records: List containing dictionaries of user records. :return: prepared context output list. """ return [ { "Id": record.get("Id", ""), "Name": record.get("Name", ""), "Description": record.get("BMCServiceDesk__broadcastDescription__c", ""), "Category": record.get("BMCServiceDesk__Category_ID__c", ""), "Status": record.get("BMCServiceDesk__Status_ID__c", ""), "Priority": record.get("BMCServiceDesk__Priority_ID__c", ""), "Urgency": record.get("BMCServiceDesk__Urgency_ID__c", ""), "Impact": record.get("BMCServiceDesk__Impact_ID__c", ""), } for record in broadcast_records ] def prepare_query_for_queue_details_get(args: dict[str, Any]) -> str: """ Prepares query for bmc-remedyforce-queue-details-get-command. :param args: Command arguments. :return: query string. """ queue_name = args.get("queue_name", "") queue_type = args.get("type", "") if queue_type: queue_name = f" and queue.name = '{queue_name}'" if queue_name else "" return ( SALESFORCE_QUERIES["GET_QUEUE_DETAIL_FOR_SPECIFIC_TYPE"].format(QUEUE_TYPES.get(queue_type, queue_type)) + queue_name ) queue_name = f" and name = '{queue_name}'" if queue_name else "" return SALESFORCE_QUERIES["GET_QUEUE_DETAIL"].format(queue_name) def prepare_queue_details_get_output(records: list[dict[str, Any]]) -> list[dict[str, Any]]: """ Prepares context output for queue_details_get command. :param records: List containing dictionaries of queue records. :return: prepared context output list. """ return [ { "Id": record.get("QueueId", "") if record.get("QueueId", "") else record.get("Id", ""), "Name": record.get("Queue", {}).get("Name", "") if record.get("Queue", {}) else record.get("Name", ""), "Email": record.get("Queue", {}).get("Email", "") if record.get("Queue", {}) else record.get("Email", ""), } for record in records ] """ REQUESTS FUNCTIONS """ @logger def process_single_service_request_definition(res) -> dict: """ Process single service request definition response object :param res: service request definition object :return: processed object for context """ # Removing unnecessary fields from questions objects required_question_fields = ["IsRequired", "Text", "Type", "Id"] filtered_questions = [ {key: value for (key, value) in question.items() if key in required_question_fields} for question in res.get("Questions", []) ] srd_context = { "Id": res.get("Id", ""), "CategoryId": res.get("CategoryId", ""), "IsProblem": res.get("IsProblem", ""), "LastModifiedDate": res.get("LastModifiedDate", ""), "CreatedDate": res.get("CreatedDate", ""), "Questions": createContext(data=filtered_questions, removeNull=True), "Conditions": createContext(data=res.get("Conditions", []), removeNull=True), } # For checking each name in field, to ensure the standard keys are not replaced in the context, # e.g. there is a field called 'id' in the fields objects standard_key_set = {key.strip().lower() for key in srd_context} # Creating a dictionary of Name: Value pairs for fields where name and value fields exist fields = { field["Name"]: field["Value"] for field in res.get("Fields", []) if "Name" in field and "Value" in field and field["Name"].strip().lower() not in standard_key_set } # Merge fields with context output srd_context.update(fields) return srd_context def process_single_service_request_definition_output(res) -> dict: """ Process single service request definition response object for output :param res: service request definition object :return: processed object for output """ service_request_definition_name = "" for field in res.get("Fields", []): if field.get("Name", "") == "title": service_request_definition_name = field.get("Value", "") questions = "" for question in res.get("Questions", []): questions += ( ("\n\n" if len(questions) > 0 else "") + "Id: " + question.get("Id", "") + "\nQuestion: " + question.get("Text", "") + "\nIs Required: " + ("Yes" if question.get("IsRequired", False) else "No") ) return { "Service Request Definition Id": res.get("Id", ""), "Service Request Definition Name": service_request_definition_name, "Questions": questions, } def prepare_context_for_get_service_request_definitions(resp: dict) -> list: """ Prepare context for get service request definition command. :param resp: Dictionary of response of the API. :return: List of objects for Context. """ return [ process_single_service_request_definition(resp.get("Result")) if isinstance(resp.get("Result"), dict) else process_single_service_request_definition(res) for res in resp.get("Result", []) ] def prepare_hr_output_for_get_service_request_definitions(resp: dict): """ Prepare hr output for get service request definition command. :param resp: Dictionary of response of the API. :return: List of objects or dictionary for output. """ if isinstance(resp.get("Result"), dict): return process_single_service_request_definition_output(resp.get("Result")) else: return [process_single_service_request_definition_output(res) for res in resp.get("Result", [])] def get_service_request_def_id_from_name(name, client) -> str: """ Get service request definition id for the passed name :param name: Service request definition name :param client: client object :return: Service request definition id if found, else an empty string :raises ConnectionError: If there is proxy error or connection error while making the http call. :raises DemistoException: If there is any other issues while making the http call or id could not be found in the response """ if name is None or len(name.strip()) < 1: return "" query = SALESFORCE_QUERIES["SERVICE_REQUEST_DEF_NAME"].format(name.strip()) query_response = client.http_request(method="GET", url_suffix=URL_SUFFIX["SALESFORCE_QUERY"], params={"q": query}) if len(query_response.get("records", [])) > 0 and isinstance(query_response.get("records", [])[0], dict): return query_response.get("records", [])[0].get("Id", 0) else: return "" def get_id_from_incident_number(client: Client, request_number: str, incident_type: str | None = None): """ Retrieve id of input request_number :param client: client object :param request_number: incident or service request number :param incident_type: incident type - IN/SR, default value being None :return: string: id of incident or service request number """ query = SALESFORCE_QUERIES.get("GET_ID_FROM_NAME", "") query = query.format(request_number) if incident_type == "IN": query += " and BMCServiceDesk__isServiceRequest__c=false" elif incident_type == "SR": query += " and BMCServiceDesk__isServiceRequest__c=true" url_suffix = URL_SUFFIX.get("SALESFORCE_QUERY", "") params = {"q": query} api_response = client.http_request("GET", url_suffix=url_suffix, params=params) if api_response.get("totalSize", 0) == 0: raise ValueError(MESSAGES["NOTE_CREATE_FAIL"].format(request_number)) output_records = api_response.get("records", "") ids = [] for record in output_records: if record.get("Id", ""): ids.append({"Id": record.get("Id", "")}) if not ids: raise ValueError(MESSAGES["NOT_FOUND_ERROR"]) return ids[0].get("Id", "") def input_data_create_note(summary: str, notes: str) -> dict: """ Format input data for create note. :param summary: summary passed by user :param notes: note passed by user :return Dict """ return {"ActivityLog": [{"Summary": summary, "Notes": notes}]} def get_request_params(data: dict[str, str], params: dict[str, Any]) -> dict[str, Any]: """ Generate request params from given data. :type data: ``dict`` :param data: Dictionary having data of additional_fields argument. :type params: ``dict`` :param params: Dictionary having data of rest of the arguments. :return: Dictionary having data in combinations of additional_fields and rest of the arguments. :rtype: ``dict`` """ for each_field in data: if data.get(each_field): params[each_field] = data.get(each_field) return params def generate_params(param: str, param_object: str, body: dict[str, str]) -> dict[str, str]: """ Generate Dictionary having key as Mapping object of field mentioned in "param_object" and value as param. :type param: ``str`` :param param: String containing value which will be assigned as value of key mentioned in param_object in body. :type param_object: ``str`` :param param_object: Key for dictionary object. :type body: ``dict`` :param body: :rtype: ``dict`` """ if MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS.get(param_object): body[MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS[param_object]] = param else: body[param_object] = param return body def get_valid_arguments(data: str, field: str) -> tuple[Any, list[str]]: """ Get dictionary structure :type data: ``str`` :param data: String from which dictionary will be made. :type field: ``str`` :param field: String containing field to raise exception. :return: Tuple containing dictionary and list. :rtype: ``tuple`` :raises ValueError: If format of data is invalid. """ excluded_fields = [] temp = {} regex_to_validate_json = re.compile(rf"{VALIDATE_JSON}") if data: if regex_to_validate_json.fullmatch(data): fields = data.split(FIELD_DELIMITER) for each_field in fields: key, value = each_field.split(VALUE_DELIMITER) if value and value.strip() != "": temp[key.strip()] = value else: excluded_fields.append(key) return temp, excluded_fields else: raise ValueError("{}".format(MESSAGES["INVALID_DATA_FORMAT"]).format(field)) else: return data, excluded_fields def remove_prefix(prefix: str, field: str) -> str: """ Remove the prefix from given field. :type prefix: ``str`` :param prefix: Prefix which will be removed from field. :type field: ``str`` :param field: String from which prefix will be removed. :return: Field after removing prefix from it. :rtype: ``str`` """ prefix = prefix.upper() field_prefix = field[:2] field_prefix = field_prefix.upper() if field_prefix == prefix: field = field[2:] return field def create_service_request( client: Client, service_request_definition: str, answers: list[dict[str, Any]], excluded_fields: list[str], additional_fields: dict[str, Any] | None, default_args: dict[str, Any], ) -> None: """ Create service request and update rest of the fields in that service request and also update context output. :type client: ``object`` :param client: Instance of Client class. :type service_request_definition: ``str`` :param service_request_definition: Name of service request definition. :type answers: ``list`` :param answers: List of dictionaries containing answers as value of key having respective question_id. :type excluded_fields: ``list`` :param excluded_fields: List of the fields which will not updated in service request. :type additional_fields: ``dict`` :param additional_fields: Dictionary containing key-value pairs which will be passed as "additional_fields" argument. :type default_args: ``dict`` :param default_args: Dictionary containing key-value pairs of default arguments of command. :raises DemistoException: If any issues will occur while making the http call to create service request. """ category_id = default_args.get("category_id") queue_id = default_args.get("queue_id") staff_id = default_args.get("staff_id") status_id = default_args.get("status_id") urgency_id = default_args.get("urgency_id") client_id = default_args.get("client_id") body = { "Fields": [ {"Name": "requestDefinitionId", "Value": service_request_definition}, {"Name": "client", "Value": client_id if client_id else ""}, ], "Answers": answers, } headers = {"Content-Type": "application/json"} response = client.http_request(method="POST", url_suffix=URL_SUFFIX["SERVICE_REQUEST"], headers=headers, json_data=body) if response and response.get("Success"): outputs = { "Number": response.get("Result", {}).get("Number", 0), "Id": response.get("Result", {}).get("Id", 0), "CreatedDate": datetime.now().strftime(DATE_FORMAT), } markdown_message = "{}".format(HR_MESSAGES["SERVICE_REQUEST_CREATE_SUCCESS"]).format( response.get("Result", {}).get("Number", 0) ) params = { "category_id": category_id, "queue_id": queue_id, "staff_id": staff_id, "status_id": status_id, "urgency_id": urgency_id, "client_id": client_id, } if additional_fields: params = get_request_params(data=additional_fields, params=params) params = remove_empty_elements(params) resp = update_incident(client, response.get("Result", {}).get("Id", 0), params=params) if resp and resp.get("message"): markdown_message = "{}".format(MESSAGES["CREATE_SERVICE_REQUEST_WARNING"]).format( response.get("Result", {}).get("Number", 0), ", ".join(params.keys()), resp.get("message") ) hr_output = {OUTPUT_PREFIX["SERVICE_REQUEST_WARNING"]: outputs} return_warning( message=markdown_message, exit=True, warning=markdown_message, outputs=hr_output, ignore_auto_extract=True ) elif excluded_fields: markdown_message = "{}".format(MESSAGES["CREATE_SERVICE_REQUEST_WARNING"]).format( response.get("Result", {}).get("Number", 0), ", ".join(excluded_fields), MESSAGES["UNEXPECTED_ERROR"] ) hr_output = {OUTPUT_PREFIX["SERVICE_REQUEST_WARNING"]: outputs} return_warning( message=markdown_message, exit=True, warning=markdown_message, outputs=hr_output, ignore_auto_extract=True ) return_results( CommandResults( outputs_prefix=OUTPUT_PREFIX["SERVICE_REQUEST"], outputs_key_field="Number", outputs=outputs, readable_output=markdown_message, raw_response=response, ) ) elif response: raise DemistoException(response.get("ErrorMessage", MESSAGES["UNEXPECTED_ERROR"])) def update_service_request( client: Client, service_request_number: str, excluded_fields: list[str], additional_fields: dict[str, str] | None, default_args: dict[str, str], ) -> None: """ Fetch respective id from given service request number and update service request and return valid context output. :type client: ``object`` :param client: Instance of Client class. :type service_request_number: ``str`` :param service_request_number: Service request number :type excluded_fields: ``list`` :param excluded_fields: List containing field which will not be updated. :type category_id: ``str`` :param category_id: Category Id :type urgency_id: ``str`` :param urgency_id: Urgency Id :type client_id: ``str`` :param client_id: Client Id :type queue_id: ``str`` :param queue_id: Queue Id :type staff_id: ``str`` :param staff_id: Staff Id :type status_id: ``str`` :param status_id: Status Id :type additional_fields: ``dict`` :param additional_fields: Dictionary containing values of rest of the fields which will be updated. :type default_args: ``dict`` :param default_args: Dictionary containing values of default fields which will be updated. :raises DemistoException: If request to update rest of the fields will fail. :raises DemistoException: If service_request_number is invalid. """ category_id = default_args.get("category_id") queue_id = default_args.get("queue_id") staff_id = default_args.get("staff_id") status_id = default_args.get("status_id") urgency_id = default_args.get("urgency_id") client_id = default_args.get("client_id") endpoint_to_get_id = SALESFORCE_QUERIES["GET_ID_FROM_SERVICE_REQUEST_NUMBER"].format(service_request_number) # Check it is service request or not and if it is then find id from Service request number response = client.http_request(method="GET", url_suffix=URL_SUFFIX["SALESFORCE_QUERY"], params={"q": endpoint_to_get_id}) if response.get("records") and response.get("records", [])[0].get("BMCServiceDesk__isServiceRequest__c"): service_request_id = response.get("records", [])[0].get("Id") else: raise DemistoException("{}".format(MESSAGES["NOT_FOUND_SERVICE_REQUEST"]).format(service_request_number)) request_params = { "category_id": category_id, "queue_id": queue_id, "staff_id": staff_id, "status_id": status_id, "urgency_id": urgency_id, "client_id": client_id, } if additional_fields: request_params = get_request_params(data=additional_fields, params=request_params) request_params = remove_empty_elements(request_params) resp = update_incident(client, service_request_id, params=request_params) resp["outputs"]["Number"] = service_request_number resp["outputs"]["Id"] = service_request_id if resp.get("message"): readable_output = HR_MESSAGES["COMMAND_FAILURE"].format(demisto.command(), resp["message"]) context_output = {OUTPUT_PREFIX["SERVICE_REQUEST_WARNING"]: resp["outputs"]} return_error(message=readable_output, error=readable_output, outputs=context_output) else: if excluded_fields: markdown_message = "{}".format(MESSAGES["UPDATE_SERVICE_REQUEST_WARNING"]).format( service_request_number, ", ".join(excluded_fields), MESSAGES["UNEXPECTED_ERROR"] ) outputs = {OUTPUT_PREFIX["SERVICE_REQUEST_WARNING"]: resp["outputs"]} return_warning( message=markdown_message, exit=True, warning=markdown_message, outputs=outputs, ignore_auto_extract=True ) else: return_results( CommandResults( outputs_prefix=OUTPUT_PREFIX["SERVICE_REQUEST"], outputs_key_field="Number", outputs=resp["outputs"], readable_output="{}".format(HR_MESSAGES["SERVICE_REQUEST_UPDATE_SUCCESS"]).format(service_request_number), ) ) def update_incident(client: Client, incident_id: str, params: dict[str, Any]) -> dict[str, Any]: """ Common method to update incident/service request. :type client: ``object`` :param client: Instance of Client class. :type incident_id: ``str`` :param incident_id: Incident Id. :type params: ``dict`` :param params: Http request params. :return: Dictionary containing context output and error messages. :rtype: ``dict`` """ body: dict[str, str] = {} outputs = {} endpoint = "{}/{}".format(URL_SUFFIX["UPDATE_INCIDENT"], incident_id) params = remove_empty_elements(params) for each_param in params: body = generate_params(params[each_param], each_param, body) headers = { "Content-Type": "application/json", } try: http_response = client.http_request(method="PATCH", url_suffix=endpoint, headers=headers, json_data=body) if isinstance(http_response, Response) and http_response.status_code == 204 and not http_response.text: outputs["LastUpdatedDate"] = datetime.now().strftime(DATE_FORMAT) return {"outputs": outputs} except DemistoException as e: message = str(e) if str(e) else MESSAGES["UNEXPECTED_ERROR"] return {"outputs": outputs, "message": message} def create_template_output(result: list[dict[str, Any]]) -> list[dict[str, Any]]: """ Prepares data for context and human readable :param result: list of raw data :return: list """ template_readable_list = [] for result_row in result: template_readable_list.append( { "Id": result_row.get("Id", ""), "Name": result_row.get("Name", ""), "Description": result_row.get(MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["description_object"], ""), "Recurring": result_row.get(MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["has_recurrence"], ""), } ) return template_readable_list def create_hr_context_output(result: list) -> list: """ For creating context and human readable :param result: list of raw data :return: list """ hr_context_output_list = [] for result_row in result: hr_context_output_list.append({"Id": result_row.get("Id", ""), "Name": result_row.get("Name", "")}) return hr_context_output_list def get_update_incident_payload(args: dict[str, str]) -> tuple[dict[str, Any], list[str]]: """ Processes command arguments for update incident api call payload :param args: Command arguments :return: Tuple containing dictionary of update request payload and list of field names to be updated """ # Update request body for default arguments update_request_body = { MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS[key]: value for (key, value) in args.items() if len(value.strip()) > 0 and key in MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS and key != "additional_fields" } # List of user friendly fields list fields = list(args.keys()) if args.get("additional_fields", "").strip() == "": return update_request_body, fields additional_fields = get_valid_arguments(args.get("additional_fields", ""), "additional_fields")[0] additional_fields_body = { (MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS.get(key, key)): value for (key, value) in additional_fields.items() } # Throw error if additional fields contain default argument fields invalid_fields = [key for (key, value) in additional_fields.items() if key in DEFAULT_INCIDENT_ARGUMENTS] if len(invalid_fields) > 0: raise DemistoException(MESSAGES["INVALID_ADDITIONAL_ARGUMENT"].format(", ".join(invalid_fields))) # Merge default fields and fields found in additional arguments update_request_body.update(additional_fields_body) fields = fields + list(additional_fields.keys()) fields.remove("additional_fields") return update_request_body, fields def validate_and_get_date_argument(args: dict[str, Any], key: str, field_name: str) -> datetime | None: """ Validates and gets a key as per one of the ALLOWED_DATE_FORMATs from the arguments, if exists. :param args: Dictionary containing date field :param key: key that contains date field :param field_name: user-friendly name of the date field :return: Date, if one could be parsed, else None :raises ValueError: if data for a date field key exists but cannot be parsed. """ if key in args: try: try: date = datetime.strptime(args[key], ALLOWED_DATE_FORMAT_1) except ValueError: try: date = datetime.strptime(args[key], ALLOWED_DATE_FORMAT_2) except ValueError: date = datetime.strptime(args[key], ALLOWED_DATE_FORMAT_3) return date except ValueError: raise ValueError(MESSAGES["DATE_PARSE_ERROR"].format(field_name)) return None def validate_incident_update_payload(payload: dict[str, Any]) -> None: """ Validates incident update payload. :param payload: incident update payload dictionary :return: None :raises ValueError: If the provided data is not valid for updating an incident. """ opened_date = validate_and_get_date_argument(payload, MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["opened_date"], "opened_date") due_date = validate_and_get_date_argument(payload, MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["due_date"], "due_date") outage_start = validate_and_get_date_argument( payload, MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["outage_start"], "outage_start" ) outage_end = validate_and_get_date_argument(payload, MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["outage_end"], "outage_end") if opened_date and due_date and not opened_date < due_date: raise ValueError(MESSAGES["DATE_VALIDATION_ERROR"].format("due_date", "opened_date")) if outage_start and outage_end and not outage_start < outage_end: raise ValueError(MESSAGES["DATE_VALIDATION_ERROR"].format("outage_end", "outage_start")) def remove_extra_space_from_args(args: dict[str, str]) -> dict[str, str]: """ Remove leading and trailing spaces from all the arguments and remove empty arguments :param args: Dictionary of arguments :return: Dictionary of arguments """ return {key: value.strip() for (key, value) in args.items() if value and len(value.strip()) > 0} def create_asset_output(result: list[dict[str, Any]], output_type: str) -> list[dict[str, str]]: """ Prepares data for context and human readable :param result: list of raw data :param output_type:to check creating context or human readable :return: list """ asset_readable_list = [] if output_type == "hr": for result_row in result: asset_readable_list.append( { "Id": result_row.get("Id", ""), "Name": result_row.get("Name", ""), "Description": result_row.get(MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["asset_description_object"], ""), "Asset #": result_row.get(MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["ci_tag"], ""), "Class Name": result_row.get(MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["class_name_object"], ""), "Instance Type": result_row.get(MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["instance_type_object"], ""), } ) else: for result_row in result: asset_readable_list.append( { "Id": result_row.get("Id", ""), "Name": result_row.get("Name", ""), "Description": result_row.get(MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["asset_description_object"], ""), "Asset_Number": result_row.get(MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["ci_tag"], ""), "Class_Name": result_row.get(MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["class_name_object"], ""), "Instance_Type": result_row.get(MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["instance_type_object"], ""), } ) return asset_readable_list def create_asset_query(asset_name: str, instance_type: str) -> str: """ Prepare query with asset_name and instance_type :param asset_name: asset name :param instance_type: asset's instance type :return: string """ append_query = "" if asset_name: append_query = append_query + SALESFORCE_QUERIES["FILTER_WITH_NAME"].format(asset_name) if instance_type == ALL_INSTANCE_TYPE["asset_classes"]: append_query = append_query + SALESFORCE_QUERIES["FILTER_ASSET_CLASSES"] elif instance_type == ALL_INSTANCE_TYPE["ci_classes"]: append_query = append_query + SALESFORCE_QUERIES["FILTER_CI_CLASSES"] elif instance_type and instance_type != "All Classes": append_query = append_query + f"and BMCServiceDesk__InstanceType__c='{instance_type}' " return append_query def prepare_query_for_user_details_get(args: dict[str, Any]) -> str: """ Prepares query for bmc-remedyforce-user-details-get-command. :param args: Command arguments. :return: query string. """ query = "" for arg_key, arg_val in args.items(): if arg_val: query += SALESFORCE_QUERIES["QUERY_AND"] if query else "" if arg_key in ["email", "username", "account_name"]: query += f"{MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS.get(arg_key, arg_key)}='{arg_val.lower()}'" elif arg_key == "queue_name": query += SALESFORCE_QUERIES["GET_USER_DETAILS_USING_QUEUE"].format(arg_val) elif arg_key == "is_staff": query += f"{MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS.get(arg_key, arg_key)}={arg_val.lower()}" query = SALESFORCE_QUERIES["QUERY_AND"] + query if query else "" return SALESFORCE_QUERIES["GET_USER_DETAILS"] + query def prepare_user_details_get_output(users_records: list[dict[str, Any]]) -> list[dict[str, Any]]: """ Prepares context output for user_details_get command. :param users_records: List containing dictionaries of user records. :return: prepared context output list. """ return [ { "Id": record.get("Id", ""), "Name": record.get("Name", ""), "FirstName": record.get("FirstName", ""), "LastName": record.get("LastName", ""), "Username": record.get("Username", ""), "Email": record.get("Email", ""), "Phone": record.get("Phone", ""), "Account": record.get("BMCServiceDesk__Account_Name__c", ""), "CompanyName": record.get("CompanyName", ""), "Division": record.get("Division", ""), "Department": record.get("Department", ""), "Title": record.get("Title", ""), "IsStaff": record.get("BMCServiceDesk__IsStaffUser__c", ""), } for record in users_records ] def prepare_note_create_output(record: dict) -> dict: """ Prepares context output for user_details_get command. :param record: Dict containing note record. :return: prepared context output Dict. """ return { "Id": record.get("Id", ""), "WorkInfoType": record.get("WorkInfoType", ""), "ViewAccess": record.get("ViewAccess", ""), "Summary": record.get("Summary", ""), "Submitter": record.get("Submitter", ""), "srId": record.get("srId", ""), "Notes": record.get("Notes", ""), "ModifiedDate": record.get("ModifiedDate", ""), "CreatedDate": record.get("CreatedDate", ""), } def get_service_request_details(client: Client, service_request_id: str) -> dict[str, str]: """ Get service request details for given service_request_id :param client: Instance of Client class. :param service_request_id: service_request id :return: Processed details of service request """ service_request_details: dict[str, str] = {} if not service_request_id or len(service_request_id.strip()) < 1: return service_request_details response = client.http_request("GET", url_suffix="{}/{}".format(URL_SUFFIX["SERVICE_REQUEST"], service_request_id)) if response and response.get("Success") and response.get("Result"): results = response["Result"] if results.get("Answers"): answers = results["Answers"] for each_answer in answers: if each_answer.get("Type") != HEADER_SECTION_TYPE: service_request_details[each_answer["QuestionText"]] = each_answer["Text"] return service_request_details def process_attachment_record(record: dict[str, Any]) -> dict[str, Any]: """ Processes single record of attachment to convert as per the custom incident layout :param record: attachment record :return: processed attachment record for markdown """ date_time = dateparser.parse(record.get("ContentDocument", {}).get("CreatedDate", "")) assert date_time is not None date = date_time.strftime(DISPLAY_DATE_FORMAT) download_url = demisto.params()["url"] + URL_SUFFIX["DOWNLOAD_ATTACHMENT"].format(record.get("ContentDocumentId", "")) attachment = { "File": record.get("ContentDocument", {}).get("Title", "NA"), "Download Link": download_url, DATE_AND_TIME: date, "Created By": record.get("ContentDocument", {}).get("CreatedBy", {}).get("Name", ""), } return attachment def get_attachments_for_incident(client: Client, incident_id: str) -> list[dict[str, Any]]: """ Get attachments for the given incident/service request id :param client: Instance of Client class. :param incident_id: incident/service_request id :return: Processed list of attachments """ attachments: list[dict] = [] if not incident_id or len(incident_id.strip()) < 1: return attachments response = client.http_request( "GET", url_suffix=URL_SUFFIX["SALESFORCE_QUERY"], params={"q": SALESFORCE_QUERIES["GET_ATTACHMENTS"].format(incident_id)} ) records = response.get("records", []) return [process_attachment_record(record) for record in records] def process_notes_record(record: dict[str, Any]) -> dict[str, str]: """ Process Note(s) record. :type record: ``str`` :param record: list of notes. :return: list """ date_time = dateparser.parse(record.get("CreatedDate", "")) assert date_time is not None date = date_time.strftime(DISPLAY_DATE_FORMAT) notes = { "Note": record.get("BMCServiceDesk__note__c", ""), DATE_AND_TIME: date, "Incident History ID": record.get("Name", ""), "Action~": record.get("BMCServiceDesk__actionId__c", ""), "Description": record.get("BMCServiceDesk__description__c", ""), "Sender": record.get("CreatedBy", {}).get("Name", ""), } return notes def get_notes_for_incident(client: Client, incident_number: str) -> list[dict[str, Any]]: """ Gets Note(s) from incident or service request. :type client: ``object`` :param client: Instance of Client class. :type incident_number: ``str`` :param incident_number: Incident or service request number. :return: list """ notes: list[dict] = [] if not incident_number or len(incident_number.strip()) < 1: return notes response = client.http_request( "GET", url_suffix=URL_SUFFIX["SALESFORCE_QUERY"], params={"q": SALESFORCE_QUERIES["GET_NOTES"].format(incident_number)} ) records = response.get("records", []) notes = [process_notes_record(record) for record in records] return notes def create_output_for_incident(result: list) -> list: """ For creating hr and context of incident :param result: list of raw data :return: list """ hr_output_list = [] for result_row in result: result_row = remove_empty_elements(result_row) hr_output_list.append( { "Number": result_row.get("Name", ""), "Priority": result_row.get(MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["incident_priority"], ""), "Description": result_row.get(MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["description"], ""), "ClientID": result_row.get(MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["incident_client_name"], ""), "Status": result_row.get(MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["status"], ""), "Staff": result_row.get("BMCServiceDesk__FKOpenBy__r", {}).get("Name", ""), "Queue": result_row.get(MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["queue"], ""), "Id": result_row.get("Id", ""), "Category": result_row.get("BMCServiceDesk__Category_ID__c", ""), "Urgency": result_row.get("BMCServiceDesk__Urgency_ID__c", ""), "dueDateTime": result_row.get("BMCServiceDesk__dueDateTime__c", ""), "ClientAccount": result_row.get("BMCServiceDesk__Client_Account__c", ""), "Broadcast": result_row.get("BMCServiceDesk__FKBroadcast__r", {}).get("Name", ""), "closeDateTime": result_row.get("BMCServiceDesk__closeDateTime__c", ""), "Asset": result_row.get("BMCServiceDesk__FKBMC_BaseElement__r", {}).get("Name", ""), "CreatedDate": result_row.get("CreatedDate", ""), "LastModifiedDate": result_row.get("LastModifiedDate", ""), "openDateTime": result_row.get("BMCServiceDesk__openDateTime__c", ""), "outageTo": result_row.get("BMCServiceDesk__outageTo__c", ""), "outageFrom": result_row.get("BMCServiceDesk__outageFrom__c", ""), "Resolution": result_row.get("BMCServiceDesk__incidentResolution__c", ""), "respondedDateTime": result_row.get("BMCServiceDesk__respondedDateTime__c", ""), "Service": result_row.get("BMCServiceDesk__FKBusinessService__r", {}).get("Name", ""), "ServiceOffering": result_row.get("BMCServiceDesk__FKServiceOffering__r", {}).get("Name", ""), "Template": result_row.get("BMCServiceDesk__FKTemplate__r", {}).get("Name", ""), "Type": result_row.get("BMCServiceDesk__Type__c", ""), "Impact": result_row.get("BMCServiceDesk__Impact_Id__c", ""), } ) return hr_output_list def prepare_outputs_for_get_service_request(records: list[dict]) -> tuple[list, list]: """ Prepares context output and human readable output for service_requests_get command. :param records: List containing dictionaries of records. :return: tuple containing context output and human readable output. """ outputs: list[dict] = [] hr_outputs: list[dict] = [] for each_record in records: context_dict: dict[str, str] = {} hr_dict: dict[str, str] = {} for each_field in FIELD_MAPPING_FOR_GET_INCIDENTS: if each_record.get(each_field): if isinstance(each_record[each_field], dict): context_dict[FIELD_MAPPING_FOR_GET_INCIDENTS[each_field]] = each_record[each_field]["Name"] else: context_dict[FIELD_MAPPING_FOR_GET_INCIDENTS[each_field]] = each_record[each_field] hr_dict["Number"] = each_record["Name"] hr_dict["Priority"] = each_record["BMCServiceDesk__Priority_ID__c"] hr_dict["Description"] = each_record["BMCServiceDesk__incidentDescription__c"] hr_dict["ClientID"] = each_record["BMCServiceDesk__Client_Name__c"] hr_dict["Status"] = each_record["BMCServiceDesk__Status_ID__c"] hr_dict["Queue"] = each_record["BMCServiceDesk__queueName__c"] if each_record.get("BMCServiceDesk__FKOpenBy__r"): hr_dict["Staff"] = each_record["BMCServiceDesk__FKOpenBy__r"]["Name"] hr_outputs.append(hr_dict) outputs.append(context_dict) return outputs, hr_outputs """ COMMAND FUNCTIONS """ def test_module(client: Client) -> None: """ Setting 'ok' result indicates that the integration works like it is supposed to. Connection to the salesforce service is successful and we can retrieve user information from the session id generated using provided parameters. Args: client: BMCHelixRemedyForce client Returns: None """ client.http_request("GET", URL_SUFFIX["TEST_MODULE"]) return_results("ok") @logger def fetch_incidents( client: Client, params: dict[str, Any], last_run: dict[str, Any], first_fetch: int ) -> tuple[dict[str, Any], list[dict[str, Any]]]: """ This function retrieves new incidents every interval. :param client: Client object. :param params: Parameters for fetch-incidents. :param last_run: A dictionary with a key containing the latest incident modified time which we got from last run. :param first_fetch: It contains the timestamp in milliseconds on when to start fetching incidents, if last_run is not provided. :returns: Tuple containing two elements. incidents list and timestamp. """ # Retrieving last run time if not none, otherwise first_fetch will be considered. start_time = last_run.get("start_time", None) start_time = int(start_time) if start_time else first_fetch incidents: list[dict[str, Any]] = [] query = prepare_query_for_fetch_incidents(params, start_time) response = client.http_request("GET", url_suffix=URL_SUFFIX["SALESFORCE_QUERY"], params={"q": query}) for record in response.get("records", []): if record.get("Id"): record["attachments"] = get_attachments_for_incident(client, record.get("Id")) if record.get("BMCServiceDesk__isServiceRequest__c"): record["service_request_details"] = get_service_request_details(client, record.get("Id")) if params.get("fetch_note", False): record["notes"] = get_notes_for_incident(client, record.get("Name", "")) incident = prepare_incident_for_fetch_incidents(record, params) incidents.append(incident) if record.get("LastModifiedDate", ""): latest_modified = date_to_timestamp(record["LastModifiedDate"], date_format="%Y-%m-%dT%H:%M:%S.%f%z") if latest_modified > start_time: start_time = latest_modified next_run = {"start_time": start_time} return next_run, incidents @logger def bmc_remedy_update_service_request_command(client: Client, args: dict[str, str]) -> None: """ To update a service request. :type client: ``object`` :param client: Instance of Client class. :type args: ``dict`` :param args: The command arguments provided by user. :raises AttributeError: If default argument fields are passed in additional_fields argument. :raises ValueError: If any invalid formatted value is given in the addition_fields argument. """ args = remove_extra_space_from_args(args) service_request_number = is_service_request_number_blank(args.get("service_request_number", "")) additional_fields, excluded_fields = get_valid_arguments(args.get("additional_fields", ""), "additional_fields") if additional_fields: if isinstance(safe_load_json(additional_fields), dict): invalid_fields = [] for each_field in additional_fields: if each_field in AVAILABLE_FIELD_LIST: invalid_fields.append(each_field) if invalid_fields: raise AttributeError( "{}".format(MESSAGES["INVALID_FIELDS_ERROR"]).format(", ".join(invalid_fields), "additional_fields") ) else: raise ValueError( "{}".format(MESSAGES["INVALID_FORMAT_ERROR"]).format("additional_fields", "field_id1=value_1; field_2=value_2") ) update_service_request( client, service_request_number, excluded_fields=excluded_fields, additional_fields=additional_fields, default_args=args ) @logger def bmc_remedy_create_service_request_command(client: Client, args: dict[str, str]) -> None: """ To create a service request. :type client: ``object`` :param client: Instance of Client class. :type args: ``dict`` :param args: The command arguments provided by user. :raises AttributeError: If any pre-available fields are passed in the additional_fields argument. :raises ValueError: If any invalid formatted value is given in the addition_fields or service_request_definition_params argument. """ args = remove_extra_space_from_args(args) answers_list = [] service_request_definition = is_parameter_blank( args.get("service_request_definition_id", ""), "service_request_definition_id" ) answers, excluded_answers = get_valid_arguments( args.get("service_request_definition_params", ""), "service_request_definition_params" ) additional_fields, excluded_fields = get_valid_arguments(args.get("additional_fields", ""), "additional_fields") if answers and isinstance(safe_load_json(answers), dict): for each_answer in answers: temp: dict[str, Any] = {} temp["Values"] = [] temp["QuestionId"] = each_answer temp["Values"].append(answers[each_answer]) answers_list.append(temp) elif answers: raise ValueError( "{}".format(MESSAGES["INVALID_FORMAT_ERROR"]).format( "service_request_definition_params", "param1=value1; param2=value2" ) ) if additional_fields and isinstance(safe_load_json(additional_fields), dict): invalid_fields = [] for each_field in additional_fields: if each_field in AVAILABLE_FIELD_LIST: invalid_fields.append(each_field) if invalid_fields: raise AttributeError( "{}".format(MESSAGES["INVALID_FIELDS_ERROR"]).format(", ".join(invalid_fields), "additional_fields") ) elif additional_fields: raise ValueError( "{}".format(MESSAGES["INVALID_FORMAT_ERROR"]).format("additional_fields", "field_id1=value_1; field_2=value_2") ) create_service_request( client, service_request_definition, answers=answers_list, additional_fields=additional_fields, excluded_fields=excluded_fields, default_args=args, ) @logger def bmc_remedy_incident_create_command(client: Client, args: dict[str, str]) -> None: """ Creates an incident. :param client: Client instance :param args: Command arguments :return: """ # Request body for create incident api call args = remove_extra_space_from_args(args) if "client_id" not in args: raise DemistoException(MESSAGES["EMPTY_REQUIRED_ARGUMENT"].format("client_id")) create_request_body = {"Description": args.pop("description", "")} # Prepare update request payload and get field names from additional arguments update_payload, fields = get_update_incident_payload(args) validate_incident_update_payload(update_payload) # Call create incident api api_response = client.http_request("POST", url_suffix=URL_SUFFIX["CREATE_INCIDENT"], json_data=create_request_body) create_result = api_response.get("Result") if not api_response.get("Success", False) or not create_result or "Id" not in create_result: raise DemistoException( HR_MESSAGES["CREATE_INCIDENT_FAILURE"].format(api_response.get("ErrorMessage", MESSAGES["UNEXPECTED_ERROR"])) ) try: id_suffix = "/{}".format(create_result.get("Id", "")) update_api_response = client.http_request("PATCH", URL_SUFFIX["UPDATE_INCIDENT"] + id_suffix, json_data=update_payload) if isinstance(update_api_response, Response) and update_api_response.status_code == 204: readable_output = HR_MESSAGES["CREATE_INCIDENT_SUCCESS"].format(create_result.get("Number", "")) return_results( CommandResults( outputs_prefix=OUTPUT_PREFIX["INCIDENT"], outputs_key_field="Id", outputs=create_result, readable_output=readable_output, raw_response=api_response, ) ) except Exception as e: readable_output = HR_MESSAGES["CREATE_INCIDENT_WARNING"].format( create_result.get("Number", ""), ", ".join(fields), str(e) ) warning_output = create_result context_output = {OUTPUT_PREFIX["INCIDENT_WARNING"]: warning_output} demisto.error(MESSAGES["TRACEBACK_MESSAGE"].format(demisto.command()) + traceback.format_exc()) # print the traceback return_warning( message=readable_output, exit=True, warning=readable_output, outputs=context_output, ignore_auto_extract=True ) @logger def bmc_remedy_incident_update_command(client: Client, args: dict[str, str]) -> None: args = remove_extra_space_from_args(args) incident_number = args.pop("incident_number", "") incident_number = incident_number[2:] if incident_number.startswith("IN") else incident_number endpoint_to_get_id = SALESFORCE_QUERIES["GET_ID_FROM_SERVICE_REQUEST_NUMBER"].format(incident_number) # Get id from incident number response = client.http_request(method="GET", url_suffix=URL_SUFFIX["SALESFORCE_QUERY"], params={"q": endpoint_to_get_id}) if response.get("records") and not response.get("records", [])[0].get("BMCServiceDesk__isServiceRequest__c"): incident_id = response.get("records", [])[0].get("Id") else: raise DemistoException("{}".format(MESSAGES["NOT_FOUND_INCIDENT"]).format(incident_number)) if not incident_id or incident_id.strip() == "": raise ValueError(MESSAGES["NOT_FOUND_ERROR"]) # Prepare update request payload and get field names from additional arguments update_payload, fields = get_update_incident_payload(args) validate_incident_update_payload(update_payload) context_output = {"Id": incident_id, "Number": incident_number} id_suffix = f"/{incident_id}" update_api_response = client.http_request("PATCH", URL_SUFFIX["UPDATE_INCIDENT"] + id_suffix, json_data=update_payload) context_output["LastUpdatedDate"] = datetime.now().strftime(DATE_FORMAT) if isinstance(update_api_response, Response) and update_api_response.status_code == 204: readable_output = HR_MESSAGES["UPDATE_INCIDENT_SUCCESS"].format(incident_number) return_results( CommandResults( outputs_prefix=OUTPUT_PREFIX["INCIDENT"], outputs_key_field="Id", outputs=context_output, readable_output=readable_output, raw_response=context_output, ) ) @logger def bmc_remedy_note_create_command(client: Client, args: dict[str, str]) -> CommandResults | None: """ Create a note for incident or service request. :param client: client object. :param args: Demisto argument(s) provided by user. :return: CommandResults which returns detailed results to war room and set context data. """ args = remove_extra_space_from_args(args) request_number = is_parameter_blank(args.get("request_number", ""), "request_number") prefix = request_number[0:2] if prefix == "IN" or prefix[0:2] == "SR": request_number = remove_prefix(prefix, request_number) incident_id = get_id_from_incident_number(client, request_number) summary = args.get("summary", "") notes = args.get("note", "") json_data = input_data_create_note(summary, notes) url_suffix = URL_SUFFIX.get("CREATE_NOTE_COMMAND", "") url_suffix = url_suffix.format(BMC_API_VERSION, incident_id) response = client.http_request("POST", url_suffix, json_data=json_data) result_flag = response.get("Result", "") result = result_flag.get("ActivityLog", [])[0] context_result = prepare_note_create_output(result) # set readable output readable_output = HR_MESSAGES["NOTE_CREATE_SUCCESS"].format(args.get("request_number", request_number)) # set Output custom_ec = createContext(data=context_result, removeNull=True) return CommandResults( outputs_prefix=OUTPUT_PREFIX["NOTE"], outputs_key_field="Id", outputs=custom_ec, readable_output=readable_output, raw_response=response, ) @logger def bmc_remedy_service_request_definition_get_command(client: Client, args: dict[str, str]) -> CommandResults | None: """ Gets service request definitions. :param client: Client instance :param args: Command arguments :return: CommandResults which returns detailed results to war room. """ args = remove_extra_space_from_args(args) service_request_definition_suffix = "" if "service_request_definition_name" in args: service_request_definition_name = args.get("service_request_definition_name") service_request_definition_id = get_service_request_def_id_from_name(service_request_definition_name, client) if service_request_definition_id == "": return CommandResults( readable_output=HR_MESSAGES["NOT_FOUND_SERVICE_REQUEST_DEF"].format(args.get("service_request_definition_name")) ) else: service_request_definition_suffix = "/" + service_request_definition_id # call api api_response = client.http_request( "GET", url_suffix=URL_SUFFIX["GET_SERVICE_REQUEST_DEFINITION"] + service_request_definition_suffix ) success = api_response.get("Success", "") if not success: raise DemistoException(MESSAGES["FAILED_MESSAGE"].format("get", "service request definition")) else: # prepare context outputs = prepare_context_for_get_service_request_definitions(api_response) custom_ec = createContext(data=outputs, removeNull=True) # prepare output output_header = MESSAGES["GET_OUTPUT_MESSAGE"].format( "service request definition(s)", 1 if isinstance(api_response.get("Result"), dict) else len(api_response.get("Result", [])), ) output_content = prepare_hr_output_for_get_service_request_definitions(api_response) # set readable output readable_output = tableToMarkdown( output_header, output_content, headers=["Service Request Definition Id", "Service Request Definition Name", "Questions"], removeNull=True, ) return CommandResults( outputs_prefix=OUTPUT_PREFIX["SERVICE_REQUEST_DEFINITION"], outputs_key_field="Id", outputs=custom_ec, readable_output=readable_output, raw_response=api_response, ) @logger def bmc_remedy_template_details_get_command(client: Client, args: dict[str, str]) -> CommandResults | str | None: """ Gets template details. :param client: client object. :param args: Demisto argument(s) provided by user. :return: CommandResults which returns detailed results to war room and set context data. """ args = remove_extra_space_from_args(args) template_name = args.get("template_name", "") query = SALESFORCE_QUERIES.get("GET_TEMPLATE_DETAILS", "") if template_name: template_name = template_name.strip() query = query + SALESFORCE_QUERIES["FILTER_WITH_NAME"].format(template_name) params = {"q": query + SALESFORCE_QUERIES["ORDER_BY_NAME"]} url_suffix = URL_SUFFIX.get("SALESFORCE_QUERY", "") response = client.http_request("GET", url_suffix=url_suffix, params=params) result = response.get("records", "") if result: template_result_list = create_template_output(result) custom_ec = createContext(data=template_result_list, removeNull=True) readable_output = tableToMarkdown( HR_MESSAGES["GET_COMMAND_DETAILS_SUCCESS"].format("template(s)", len(template_result_list)), template_result_list, headers=["Id", "Name", "Description", "Recurring"], removeNull=True, ) return CommandResults( outputs_prefix=OUTPUT_PREFIX["TEMPLATE"], outputs_key_field="Id", outputs=custom_ec, readable_output=readable_output, raw_response=response, ) else: if template_name: return MESSAGES["INVALID_ENTITY_NAME"].format("template_name", template_name) else: return MESSAGES["NO_ENTITY_FOUND"].format("template(s)") @logger def bmc_remedy_service_offering_details_get_command(client: Client, args: dict) -> CommandResults | str | None: """ Gets service offering details :param client: client object. :param args: Demisto argument(s) provided by user. :return: CommandResults which returns detailed results to war room and set context data. """ args = remove_extra_space_from_args(args) service_offering_name = args.get("service_offering_name", "") query = SALESFORCE_QUERIES.get("GET_SERVICE_OFFERING_DETAILS", "") if service_offering_name: query = query + SALESFORCE_QUERIES["FILTER_WITH_NAME"].format(service_offering_name) params = {"q": query + SALESFORCE_QUERIES["ORDER_BY_NAME"]} url_suffix = URL_SUFFIX.get("SALESFORCE_QUERY", "") response = client.http_request("GET", url_suffix=url_suffix, params=params) result = response.get("records") if result: service_offering_result_list = create_hr_context_output(result) custom_ec = createContext(data=service_offering_result_list, removeNull=True) readable_output = tableToMarkdown( HR_MESSAGES["GET_COMMAND_DETAILS_SUCCESS"].format("service offering(s)", len(service_offering_result_list)), service_offering_result_list, headers=["Id", "Name"], removeNull=True, ) return CommandResults( outputs_prefix=OUTPUT_PREFIX["SERVICE_OFFERING"], outputs_key_field="Id", outputs=custom_ec, readable_output=readable_output, raw_response=response, ) else: if service_offering_name: return MESSAGES["INVALID_ENTITY_NAME"].format("service_offering_name", service_offering_name) else: return MESSAGES["NO_ENTITY_FOUND"].format("service offering(s)") @logger def bmc_remedy_asset_details_get_command(client: Client, args: dict[str, str]) -> CommandResults | str | None: """ Gets asset details. :param client: Client instance :param args: Command arguments :return: CommandResults which returns detailed results to war room. """ args = remove_extra_space_from_args(args) asset_name = args.get("asset_name", "") instance_type = args.get("instance_type", ALL_INSTANCE_TYPE["all_classes"]) query = SALESFORCE_QUERIES.get("GET_ASSET_DETAILS", "") query = query + create_asset_query(asset_name, instance_type) url_suffix = URL_SUFFIX.get("SALESFORCE_QUERY", "") params = {"q": query + SALESFORCE_QUERIES["ORDER_BY_NAME"]} response = client.http_request("GET", url_suffix=url_suffix, params=params) result = response.get("records", "") if result: assets_result_list_hr = create_asset_output(result, "hr") assets_result_list_context = create_asset_output(result, "ct") custom_ec = createContext(data=assets_result_list_context, removeNull=True) readable_output = tableToMarkdown( HR_MESSAGES["GET_COMMAND_DETAILS_SUCCESS"].format("asset(s)", len(assets_result_list_hr)), assets_result_list_hr, headers=["Id", "Name", "Description", "Asset #", "Class Name", "Instance Type"], removeNull=True, ) return CommandResults( outputs_prefix=OUTPUT_PREFIX["ASSET"], outputs_key_field="Id", outputs=custom_ec, readable_output=readable_output, raw_response=response, ) else: if asset_name or (instance_type and instance_type != ALL_INSTANCE_TYPE["all_classes"]): return HR_MESSAGES["NO_ASSETS_FOUND"] else: return MESSAGES["NO_ENTITY_FOUND"].format("asset(s)") @logger def bmc_remedy_impact_details_get_command(client: Client, args: dict[str, str]) -> CommandResults | str | None | None: """ To get details of impact. :type client: ``object`` :param client: Instance of Client class. :type args: ``dict`` :param args: The command arguments provided by user. :raises DemistoException: If no records will be found for impacts. :raises DemistoException: If any error occurs while execution of API to get impacts. """ args = remove_extra_space_from_args(args) endpoint_to_get_impacts = SALESFORCE_QUERIES["GET_IMPACTS"] impact_name = args.get("impact_name") if impact_name: impact_name = impact_name.strip() endpoint_to_get_impacts = f"{endpoint_to_get_impacts} and name='{impact_name}'" api_response = client.http_request("GET", url_suffix=URL_SUFFIX["SALESFORCE_QUERY"], params={"q": endpoint_to_get_impacts}) records = api_response.get("records") if records: outputs = [] for each_record in records: temp = {} temp["Id"] = each_record.get("Id") temp["Name"] = each_record.get("Name") outputs.append(temp) markdown = HR_MESSAGES["GET_COMMAND_DETAILS_SUCCESS"].format("impact(s)", len(outputs)) readable_output = tableToMarkdown(markdown, outputs, headers=["Id", "Name"], removeNull=True) return CommandResults( outputs_prefix=OUTPUT_PREFIX["IMPACT"], outputs_key_field="Id", outputs=outputs, readable_output=readable_output, raw_response=records, ) else: if impact_name: return MESSAGES["INVALID_ENTITY_NAME"].format("impact_name", impact_name) else: return MESSAGES["NO_ENTITY_FOUND"].format("impact(s)") @logger def bmc_remedy_account_details_get_command(client: Client, args: dict[str, str]) -> CommandResults | str | None: """ Gets account details. :param client: Client instance :param args: Command arguments :return: CommandResults which returns detailed results to war room. """ args = remove_extra_space_from_args(args) account_name = args.get("account_name", "") query = SALESFORCE_QUERIES.get("GET_ACCOUNT_DETAILS", "") if account_name: query = query + f" and name ='{account_name}'" url_suffix = URL_SUFFIX.get("SALESFORCE_QUERY", "") params = {"q": query + SALESFORCE_QUERIES["ORDER_BY_NAME"]} response = client.http_request("GET", url_suffix=url_suffix, params=params) result = response.get("records", "") if result: accounts_result_list = create_hr_context_output(result) custom_ec = createContext(data=accounts_result_list, removeNull=True) readable_output = tableToMarkdown( HR_MESSAGES["GET_COMMAND_DETAILS_SUCCESS"].format("account(s)", len(result)), accounts_result_list, headers=["Id", "Name"], removeNull=True, ) return CommandResults( outputs_prefix=OUTPUT_PREFIX["ACCOUNT"], outputs_key_field="Id", outputs=custom_ec, readable_output=readable_output, raw_response=response, ) else: if account_name: return MESSAGES["INVALID_ENTITY_NAME"].format("account_name", account_name) else: return MESSAGES["NO_ENTITY_FOUND"].format("account(s)") @logger def bmc_remedy_status_details_get_command(client: Client, args: dict[str, str]) -> CommandResults: """ Get status details. :param client: Client object. :param args: Demisto arguments. :return: CommandResult object. """ args = remove_extra_space_from_args(args) query = SALESFORCE_QUERIES["GET_STATUS"] query += " and name='{}'".format(args["status_name"]) if "status_name" in args else "" api_response = client.http_request("GET", url_suffix=URL_SUFFIX["SALESFORCE_QUERY"], params={"q": query}) records = api_response.get("records", []) if len(records) == 0: if "status_name" in args: return CommandResults(readable_output=HR_MESSAGES["NOT_FOUND_FOR_ARGUMENTS"].format("status")) else: return CommandResults(readable_output=MESSAGES["NO_ENTITY_FOUND"].format("status")) output = [{key: value for (key, value) in record.items() if key == "Name" or key == "Id"} for record in records] markdown = HR_MESSAGES["GET_COMMAND_DETAILS_SUCCESS"].format("status", len(output)) readable_output = tableToMarkdown(markdown, output, headers=["Id", "Name"], removeNull=True) return CommandResults( outputs_prefix=OUTPUT_PREFIX["STATUS"], outputs_key_field="Id", outputs=output, readable_output=readable_output, raw_response=records, ) @logger def bmc_remedy_urgency_details_get_command(client: Client, args: dict[str, str]) -> CommandResults | str | None: """ Gets urgency details. :param client: Client instance :param args: Command arguments :return: CommandResults which returns detailed results to war room. """ args = remove_extra_space_from_args(args) urgency_name = args.get("urgency_name", "") query = SALESFORCE_QUERIES.get("GET_URGENCY_DETAILS", "") if urgency_name: query = query + SALESFORCE_QUERIES["FILTER_WITH_NAME"].format(urgency_name) params = {"q": query} url_suffix = URL_SUFFIX.get("SALESFORCE_QUERY", "") response = client.http_request("GET", url_suffix=url_suffix, params=params) result = response.get("records") if result: urgency_result_list = create_hr_context_output(result) custom_ec = createContext(data=urgency_result_list, removeNull=True) readable_output = tableToMarkdown( HR_MESSAGES["GET_COMMAND_DETAILS_SUCCESS"].format("urgencies", len(urgency_result_list)), urgency_result_list, headers=["Id", "Name"], removeNull=True, ) return CommandResults( outputs_prefix=OUTPUT_PREFIX["URGENCY"], outputs_key_field="Id", outputs=custom_ec, readable_output=readable_output, raw_response=response, ) else: if urgency_name: return MESSAGES["INVALID_ENTITY_NAME"].format("urgency_name", urgency_name) else: return MESSAGES["NO_ENTITY_FOUND"].format("urgency") @logger def bmc_remedy_category_details_get_command(client: Client, args: dict[str, str]) -> CommandResults | str | None | None: """ To get details of categories. :type client: ``object`` :param client: Instance of Client class. :type args: ``dict`` :param args: The command arguments provided by user. :raises DemistoException: If exception will occur while rest calls. :raises ValueError: If any invalid value is given in the type argument. """ args = remove_extra_space_from_args(args) category_type = args.get("type") category_name = args.get("category_name") endpoint_to_get_category = SALESFORCE_QUERIES["GET_CATEGORIES"] error_message = MESSAGES["NO_ENTITY_FOUND"].format("category") if category_name: endpoint_to_get_category = f"{endpoint_to_get_category} and name='{category_name}'" error_message = HR_MESSAGES["NOT_FOUND_FOR_ARGUMENTS"].format("category") if category_type in POSSIBLE_CATEGORY_TYPES: if category_type == "Service Request": endpoint_to_get_category = f"{endpoint_to_get_category} and {SERVICE_REQUEST_CATEGORY_OBJECT}= true" elif category_type == "Incident": endpoint_to_get_category = f"{endpoint_to_get_category} and {INCIDENT_CATEGORY_OBJECT}= true" elif category_type: raise ValueError( "{}".format(MESSAGES["INVALID_TYPE_FOR_CATEGORIES"]).format("type", "type", ", ".join(POSSIBLE_CATEGORY_TYPES)) ) api_response = client.http_request("GET", url_suffix=URL_SUFFIX["SALESFORCE_QUERY"], params={"q": endpoint_to_get_category}) records = api_response.get("records") if records: hr_output, outputs = prepare_outputs_for_categories(records) markdown = HR_MESSAGES["GET_COMMAND_DETAILS_SUCCESS"].format("categories", len(hr_output)) readable_output = tableToMarkdown(markdown, hr_output, headers=["Id", "Name", "Children Count"], removeNull=True) return CommandResults( outputs_prefix=OUTPUT_PREFIX["CATEGORY"], outputs_key_field="Id", outputs=outputs, readable_output=readable_output, raw_response=api_response, ) else: return error_message @logger def bmc_remedy_queue_details_get_command(client: Client, args: dict[str, Any]) -> CommandResults | str: """ Get queue(s) details. :param client: Client object. :param args: demisto arguments. :return: Command Result. """ args = remove_extra_space_from_args(args) query = prepare_query_for_queue_details_get(args) response = client.http_request(method="GET", url_suffix=URL_SUFFIX["SALESFORCE_QUERY"], params={"q": query}) if not response.get("totalSize", 0): return HR_MESSAGES["NO_QUEUE_FOUND"] output = prepare_queue_details_get_output(response.get("records", [])) custom_ec = createContext(output, removeNull=True) readable_output = tableToMarkdown( HR_MESSAGES["GET_COMMAND_DETAILS_SUCCESS"].format("queue(s)", response.get("totalSize", 0)), custom_ec, ["Id", "Name", "Email"], removeNull=True, ) return CommandResults( outputs_prefix=OUTPUT_PREFIX["QUEUE"], outputs_key_field="Id", outputs=custom_ec, readable_output=readable_output, raw_response=response, ) @logger def bmc_remedy_user_details_get_command(client: Client, args: dict[str, Any]) -> CommandResults | str: """ Get user details. :param client: Client object. :param args: command arguments. :return: CommandResults object with context and human-readable. """ args = remove_extra_space_from_args(args) query = prepare_query_for_user_details_get(args) response = client.http_request(method="GET", url_suffix=URL_SUFFIX["SALESFORCE_QUERY"], params={"q": query}) if not response.get("totalSize", 0): return HR_MESSAGES["NO_USERS_FOUND"] output = prepare_user_details_get_output(response.get("records", [])) custom_ec = createContext(output, removeNull=True) readable_output = tableToMarkdown( HR_MESSAGES["GET_COMMAND_DETAILS_SUCCESS"].format("user(s)", response.get("totalSize", 0)), custom_ec, [ "Id", "Username", "FirstName", "LastName", "Account", "Phone", "Email", "Title", "CompanyName", "Division", "Department", "IsStaff", ], removeNull=True, headerTransform=pascalToSpace, ) return CommandResults( outputs_prefix=OUTPUT_PREFIX["USER"], outputs_key_field="Id", outputs=custom_ec, readable_output=readable_output, raw_response=response, ) @logger def bmc_remedy_broadcast_details_get_command(client: Client, args: dict[str, str]) -> CommandResults | str | None: """ Get broadcast details. :type client: ``object`` :param client: Instance of Client class. :type args: ``dict`` :param args: The command arguments provided by user. :raises DemistoException: If exception will occur while rest calls. """ args = remove_extra_space_from_args(args) endpoint_to_get_broadcast = SALESFORCE_QUERIES["GET_BROADCAST_DETAILS"] broadcast_name = args.get("broadcast_name") category_name = args.get("category_name") if broadcast_name: endpoint_to_get_broadcast = "{}{}name='{}'".format( endpoint_to_get_broadcast, SALESFORCE_QUERIES["QUERY_AND"], broadcast_name ) if category_name: endpoint_to_get_broadcast = "{}{}{}='{}'".format( endpoint_to_get_broadcast, SALESFORCE_QUERIES["QUERY_AND"], MAPPING_OF_FIELDS_WITH_SALESFORCE_COLUMNS["category"], category_name, ) response = client.http_request( method="GET", url_suffix=URL_SUFFIX["SALESFORCE_QUERY"], params={"q": endpoint_to_get_broadcast} ) if response.get("records"): output = prepare_broadcast_details_get_output(response.get("records")) custom_ec = createContext(output, removeNull=True) markdown_message = HR_MESSAGES["GET_COMMAND_DETAILS_SUCCESS"].format("broadcast(s)", len(response.get("records"))) readable_output = tableToMarkdown( markdown_message, custom_ec, headers=["Id", "Name", "Description", "Priority", "Urgency", "Impact", "Category", "Status"], removeNull=True, headerTransform=pascalToSpace, ) return CommandResults( outputs_prefix=OUTPUT_PREFIX["BROADCAST"], outputs_key_field="Id", outputs=custom_ec, readable_output=readable_output, raw_response=response, ) else: return HR_MESSAGES["NO_BROADCAST_DETAILS_FOUND"] @logger def bmc_remedy_incident_get_command(client: Client, args: dict[str, str]) -> CommandResults | str | None: """ Gets Incident details. :param client: Client instance :param args: Command arguments :return: CommandResults which returns detailed results to war room. """ args = remove_extra_space_from_args(args) incident_time = args.get("last_fetch_time") incident_number = args.get("incident_number") maximum_incident = args.get("maximum_incident", 50) query = "" if incident_number: incident_number = remove_prefix("IN", incident_number) query = query + " name='{}'{}".format(incident_number, SALESFORCE_QUERIES["QUERY_AND"]) if incident_time: start_time, _ = parse_date_range(incident_time, date_format=DATE_FORMAT, utc=True) query = query + "LastModifiedDate > {}{}".format(start_time, SALESFORCE_QUERIES["QUERY_AND"]) final_query = SALESFORCE_QUERIES.get("GET_INCIDENTS", "").format(query, "false", "No") if maximum_incident: try: maximum_incident_int = int(maximum_incident) except ValueError: raise ValueError(MESSAGES["MAX_INCIDENT_LIMIT"].format("maximum_incident")) if not (1 <= int(maximum_incident_int) <= 500): raise ValueError(MESSAGES["MAX_INCIDENT_LIMIT"].format("maximum_incident")) final_query = final_query + f" LIMIT {maximum_incident_int}" response = client.http_request("GET", url_suffix=URL_SUFFIX["SALESFORCE_QUERY"], params={"q": final_query}) if response and response.get("records", ""): records = response["records"] incident_result_output = create_output_for_incident(records) incident_result_ec = createContext(data=incident_result_output, removeNull=True) readable_output = tableToMarkdown( HR_MESSAGES["GET_COMMAND_DETAILS_SUCCESS"].format("incident(s)", len(incident_result_output)), incident_result_output, headers=["Number", "Priority", "Description", "ClientID", "Status", "Staff", "Queue"], removeNull=True, headerTransform=pascalToSpace, ) return CommandResults( outputs_prefix=OUTPUT_PREFIX["INCIDENT"], outputs_key_field="Id", outputs=incident_result_ec, readable_output=readable_output, raw_response=response, ) else: return HR_MESSAGES["NO_INCIDENT_DETAILS_FOUND"] def bmc_remedy_service_request_get_command(client: Client, args: dict[str, Any]) -> CommandResults | str | None: """ Get service request details. :type client: ``object`` :param client: Instance of Client class. :type args: ``dict`` :param args: The command arguments provided by user. :raises DemistoException: If exception will occur during http calls. :raises ValueError: If value of 'maximum_service_request' parameter will be invalid. """ args = remove_extra_space_from_args(args) query = "" service_request_number = args.get("service_request_number") from_time = args.get("last_fetch_time") maximum_service_request = args.get("maximum_service_request", 50) if from_time: start_time, _ = parse_date_range(from_time, date_format=DATE_FORMAT, utc=True) query = "{} LastModifiedDate > {}{}".format(query, start_time, SALESFORCE_QUERIES["QUERY_AND"]) if service_request_number: service_request_number = remove_prefix("sr", service_request_number.strip()) query = "{}name='{}'{}".format(query, service_request_number, SALESFORCE_QUERIES["QUERY_AND"]) final_query = SALESFORCE_QUERIES["GET_SERVICE_REQUEST"].format(query, "true", "Yes") if maximum_service_request: try: maximum_service_request_int = int(maximum_service_request) except ValueError: raise ValueError(MESSAGES["MAX_INCIDENT_LIMIT"].format("maximum_service_request")) if not (1 <= maximum_service_request_int <= 500): raise ValueError(MESSAGES["MAX_INCIDENT_LIMIT"].format("maximum_service_request")) final_query = f"{final_query} LIMIT {maximum_service_request_int}" response = client.http_request("GET", url_suffix=URL_SUFFIX["SALESFORCE_QUERY"], params={"q": final_query}) if response and response.get("records"): records = response["records"] outputs, hr_outputs = prepare_outputs_for_get_service_request(records) custom_ec = createContext(hr_outputs, removeNull=True) markdown_message = HR_MESSAGES["GET_COMMAND_DETAILS_SUCCESS"].format("service request(s)", len(records)) readable_output = tableToMarkdown( markdown_message, custom_ec, headers=["Number", "Priority", "Description", "ClientID", "Status", "Staff", "Queue"], removeNull=True, headerTransform=pascalToSpace, ) return CommandResults( outputs_prefix=OUTPUT_PREFIX["SERVICE_REQUEST"], outputs_key_field="Number", outputs=outputs, readable_output=readable_output, raw_response=response, ) else: return HR_MESSAGES["NO_SERVICE_REQUEST_DETAILS_FOUND"] def init_globals(params): global SOAP_LOGIN_URL auth_url = params.get("auth_url") if auth_url: SOAP_LOGIN_URL = path.join(auth_url, f"services/Soap/u/{LOGIN_API_VERSION}") else: SOAP_LOGIN_URL = f"https://login.salesforce.com/services/Soap/u/{LOGIN_API_VERSION}" def main(): """ PARSE AND VALIDATE INTEGRATION PARAMS """ global SOAP_LOGIN_URL # Commands dictionary commands: dict[str, Callable] = { "bmc-remedy-service-request-definition-get": bmc_remedy_service_request_definition_get_command, "bmc-remedy-note-create": bmc_remedy_note_create_command, "bmc-remedy-service-offering-details-get": bmc_remedy_service_offering_details_get_command, "bmc-remedy-template-details-get": bmc_remedy_template_details_get_command, "bmc-remedy-impact-details-get": bmc_remedy_impact_details_get_command, "bmc-remedy-asset-details-get": bmc_remedy_asset_details_get_command, "bmc-remedy-queue-details-get": bmc_remedy_queue_details_get_command, "bmc-remedy-account-details-get": bmc_remedy_account_details_get_command, "bmc-remedy-user-details-get": bmc_remedy_user_details_get_command, "bmc-remedy-status-details-get": bmc_remedy_status_details_get_command, "bmc-remedy-urgency-details-get": bmc_remedy_urgency_details_get_command, "bmc-remedy-category-details-get": bmc_remedy_category_details_get_command, "bmc-remedy-broadcast-details-get": bmc_remedy_broadcast_details_get_command, "bmc-remedy-incident-get": bmc_remedy_incident_get_command, "bmc-remedy-service-request-get": bmc_remedy_service_request_get_command, } commands_without_return_result: dict[str, Callable] = { "bmc-remedy-service-request-create": bmc_remedy_create_service_request_command, "bmc-remedy-service-request-update": bmc_remedy_update_service_request_command, "bmc-remedy-incident-create": bmc_remedy_incident_create_command, "bmc-remedy-incident-update": bmc_remedy_incident_update_command, } command = demisto.command() LOG(f"Command being called is {command}") try: params = demisto.params() init_globals(params) # Username and password from credentials username = params.get("username_creds", {}).get("identifier") or params.get("username") password = params.get("username_creds", {}).get("password") or params.get("password") # Get the service API base url base_url = params["url"] # Certificate verification setting verify_certificate = not params.get("insecure", False) # System proxy settings proxy = params.get("proxy", False) # Get request timeout request_timeout = get_request_timeout() # Validating params for fetch-incidents. validate_params_for_fetch_incidents(params) # Get first fetch time from integration params. first_fetch_time = params.get("first_fetch") or "10 minutes" # getting numeric value from string representation start_time, _ = parse_date_range(first_fetch_time, date_format=DATE_FORMAT, utc=True) client = Client( base_url=base_url, verify=verify_certificate, proxy=proxy, username=username, password=password, request_timeout=request_timeout, ) if command == "test-module": # This is the call made when pressing the integration Test button. test_module(client) if command == "fetch-incidents": next_run, incidents = fetch_incidents( client=client, params=demisto.params(), last_run=demisto.getLastRun(), first_fetch=date_to_timestamp(start_time, date_format=DATE_FORMAT), ) # saves next_run for the time fetch-incidents is invoked. demisto.setLastRun(next_run) demisto.incidents(incidents) elif command in commands_without_return_result: commands_without_return_result[command](client, demisto.args()) elif command in commands: return_results(commands[command](client, demisto.args())) # Log exceptions except Exception as e: demisto.error(MESSAGES["TRACEBACK_MESSAGE"].format(demisto.command()) + traceback.format_exc()) # print the traceback if command == "test-module": return_error(str(e)) else: return_error(HR_MESSAGES["COMMAND_FAILURE"].format(demisto.command(), str(e))) if __name__ in ("__main__", "__builtin__", "builtins"): main()