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

IDBMCHelixRemedyforce
ProviderBMC Software
CategoryUtilities
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix 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 Email 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 Email
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 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 Email 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 URL
  • username — Username
  • password — Password
  • username_creds — Username
  • type — Type
  • category — Category
  • impact — Impact
  • urgency — Urgency
  • status — Status
  • queue — Queue
  • max_fetch — Max Incidents
  • fetch_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 type
  • incidentFetchInterval — Incidents Fetch Interval
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • request_timeout — HTTP(S) Request Timeout (in seconds) (required)
  • isFetch — Fetch incidents
  • first_fetch — First Fetch Timestamp (<number> <time unit>, e.g., 12 hours, 7 days, 3 months, 1 year)

Commands (19)

  • bmc-remedy-account-details-get

    Returns account details for incidents and service requests.

  • bmc-remedy-asset-details-get

    Returns asset or configuration item details for incidents.

  • bmc-remedy-broadcast-details-get

    Returns 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-get

    Returns 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-get

    Returns the impact details for incidents and service requests. Impact helps calculate the priority of the incident or service request.

  • bmc-remedy-incident-create

    Creates a new incident.

  • bmc-remedy-incident-get

    Returns details of incidents.

  • bmc-remedy-incident-update

    Updates details of an incident for a given incident number.

  • bmc-remedy-note-create

    Creates notes for incidents and service requests.

  • bmc-remedy-queue-details-get

    Returns queue details for incidents and service requests.

  • bmc-remedy-service-offering-details-get

    Returns service offering details for incidents.

  • bmc-remedy-service-request-create

    Creates 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-get

    Returns details of service request definitions using the Service Request Definition name.

  • bmc-remedy-service-request-get

    Returns the service request details.

  • bmc-remedy-service-request-update

    Updates details of a service request for a service request number.

  • bmc-remedy-status-details-get

    Returns 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-get

    Returns template details for incidents. Templates enable users to prepopulate commonly used fields in a form.

  • bmc-remedy-urgency-details-get

    Returns urgency details for incidents and service requests. Urgency determines the priority of the incident or service request.

  • bmc-remedy-user-details-get

    Returns 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()