AtlassianJiraServiceManagement
Use this integration to manage Jira objects and attach files to Jira objects from Cortex XSOAR.
IT Services · Atlassian Jira Service Management
Details
| ID | AtlassianJiraServiceManagement |
|---|---|
| Provider | Atlassian |
| Category | IT Services |
| From Version | 6.10.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
Use this integration to manage Jira objects and attach files to Jira objects from Cortex XSOAR.
This integration was integrated and tested with version 5.4.15 of AtlassianJiraServiceManagement.
Configure Atlassian Jira Service Management in Cortex
| Parameter | Required |
|---|---|
| Server URL | True |
| API Token | True |
| Trust any certificate (not secure) | False |
| Use system proxy settings | False |
Commands
You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.
jira-asset-object-schema-list
List all object schemas.
Base Command
jira-asset-object-schema-list
Input
| Argument Name | Description | Required |
|---|---|---|
| limit | The maximum amount of schemas to retrieve. Cannot be used alongside the page and page_size arguments. | Optional |
| all_results | Whether to retrieve all object schemas with no limit. Possible values are: true, false. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| JiraAsset.Schema.ID | Number | The ID of the object schema. |
| JiraAsset.Schema.Name | String | The name of the object schema. |
| JiraAsset.Schema.Key | String | The key of the object schema. |
| JiraAsset.Schema.Status | String | The status of the object schema. |
| JiraAsset.Schema.Created | Date | The date in which this object schema was created. |
| JiraAsset.Schema.Updated | Date | The date in which this object schema was updated. |
| JiraAsset.Schema.ObjectCount | Number | The number of objects in the schema. |
| JiraAsset.Schema.ObjectTypeCount | Number | The number of different object types in the schema. |
jira-asset-object-type-list
List all object types.
Base Command
jira-asset-object-type-list
Input
| Argument Name | Description | Required |
|---|---|---|
| schema_id | The schema from which the command will retrieve object types. Values can be received by running the jira-asset-object-schema-list command. |
Required |
| exclude | Object types with that name will be excluded from the results. | Optional |
| limit | The maximum amount of object types to retrieve. | Optional |
| all_results | Whether to retrieve all object types with no limit. Possible values are: true, false. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| JiraAsset.ObjectType.ID | Number | The ID of the object type. |
| JiraAsset.ObjectType.Name | String | The name of the object type. |
| JiraAsset.ObjectType.Type | Number | The type of the object type. |
| JiraAsset.ObjectType.Position | Number | The position of the object type among other object types with the same level in the schema hierarchy. |
| JiraAsset.ObjectType.Created | Date | The date the object type was created. |
| JiraAsset.ObjectType.Updated | Date | The date the object type was last updated. |
| JiraAsset.ObjectType.ObjectCount | Number | The amount of objects listed under this object type. |
| JiraAsset.ObjectType.ObjectSchemaId | Number | The ID of the schema this object type is listed under. |
| JiraAsset.ObjectType.Inherited | Boolean | Whether this object type inherits from a parent object type. |
| JiraAsset.ObjectType.AbstractObjectType | Boolean | Is this object type abstract (can only be inherited). |
| JiraAsset.ObjectType.ParentObjectTypeInherited | Boolean | Whether this object type inherits from a parent object type. |
jira-asset-object-type-attribute-list
List all attributes for an object type.
Base Command
jira-asset-object-type-attribute-list
Input
| Argument Name | Description | Required |
|---|---|---|
| object_type_id | The object type for which the command will retrieve attributes. Values can be received by running the jira-asset-object-type-list command. |
Required |
| is_editable | Whether to only return attributes that can be edited. Possible values are: true, false. | Optional |
| order_by_name | Whether to sort the results by the attribute name. Possible values are: true, false. | Optional |
| query | Query to filter on available object type attributes. Filter attributes that start with the query. | Optional |
| include_value_exist | Should the response only include attributes where attribute values exists. Possible values are: true, false. | Optional |
| exclude_parent_attributes | Should the response exclude parent attributes. Possible values are: true, false. | Optional |
| include_children | Should the response include child attributes. Possible values are: true, false. | Optional |
| order_by_required | Should the response be ordered by the number of required attributes. Possible values are: true, false. | Optional |
| limit | The maximum amount of object types to retrieve. | Optional |
| all_results | Whether to retrieve all object types with no limit. Possible values are: true, false. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| JiraAsset.Attribute.ID | Number | The ID of the attribute. |
| JiraAsset.Attribute.Name | String | The name of the attribute. |
| JiraAsset.Attribute.Label | Boolean | Whether this attribute is used as the label for objects of that type. |
| JiraAsset.Attribute.Type | String | The type of the attribute (Default, Reference, User, Project etc.). |
| JiraAsset.Attribute.DefaultType.id | Number | ID for the sub-type of the default type. |
| JiraAsset.Attribute.DefaultType.name | String | Name for the sub-type of the default type. |
| JiraAsset.Attribute.Editable | Boolean | Whether this attribute is editable. |
| JiraAsset.Attribute.System | Boolean | Whether this attribute is a system attribute or a custom one. |
| JiraAsset.Attribute.Sortable | Boolean | Can the objects be sorted by that attribute. |
| JiraAsset.Attribute.Summable | Boolean | Can this attribute be summarized. |
| JiraAsset.Attribute.Indexed | Boolean | Is this attribute indexed. |
| JiraAsset.Attribute.MinimumCardinality | Number | The minimum amount of elements this attribute should populate. |
| JiraAsset.Attribute.MaximumCardinality | Number | The maximum amount of elements this attribute should populate. |
| JiraAsset.Attribute.Removable | Boolean | Whether this attribute can be removed from the object. |
| JiraAsset.Attribute.Hidden | Boolean | Whether this attribute is a hidden attribute. |
| JiraAsset.Attribute.IncludeChildObjectTypes | Boolean | Whether this attribute includes child object types. |
| JiraAsset.Attribute.UniqueAttribute | Boolean | Whether this attribute is unique to its object. |
| JiraAsset.Attribute.Options | String | Options for the attributes. |
| JiraAsset.Attribute.Position | Number | The position of the attribute in relation to other object attributes. |
jira-asset-object-create
Create a new object of the specified object type ID. Either attributes or attributes_json must be provided.
Base Command
jira-asset-object-create
Input
| Argument Name | Description | Required |
|---|---|---|
| object_type_id | The object type for which a new object will be created. | Required |
| attributes | Provide this field if attributes_json is not provided. A key-value map of object attributes. The structure of the field is: { “attributeId1”: [ “value1”, “value2”] , “attributeId2”:[“value1”, “value2”] }. You can run the jira-asset-object-type-attribute-list command to retrieve the list of available attributes. |
Optional |
| attributes_json | Provide this field if attributes is not provided. A JSON string of object attributes to be added to the new object. Take a look at https://docs.atlassian.com/assets/REST/10.7.0/#object-createObject for the structure of this field. You can use the jira-asset-object-type-attribute-list command to retrieve the list of available attributes. |
Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| JiraAsset.Object.ID | Number | ID of the newly created object. |
| JiraAsset.Object.Label | String | The value of the attribute that is marked as Label for this object type. |
| JiraAsset.Object.ObjectKey | String | The auto-generated object key. |
| JiraAsset.Object.Avatar.objectId | Number | ID of the newly created object. |
| JiraAsset.Object.Created | Date | The date in which this object was created. |
| JiraAsset.Object.Updated | Date | The date in which this object was last updated. |
| JiraAsset.Object.HasAvatar | Boolean | Does this object have an Avatar. |
| JiraAsset.Object.Timestamp | Date | Epoch version of the object’s creation date. |
| JiraAsset.Object.Links.self | String | The link to view this object in the Jira web app. |
| JiraAsset.Object.Name | String | The name of the newly created object. |
jira-asset-object-update
Updates an existing object of specified object type ID. Either attributes or attributes_json must be provided.
Base Command
jira-asset-object-update
Input
| Argument Name | Description | Required |
|---|---|---|
| object_id | ID of the object that will be updated. | Required |
| attributes | Provide this field if attributes_json is not provided. A key-value map of object attributes. The structure of the field is: { “attributeId1”: [ “value1”, “value2”] , “attributeId2”:[“value1”, “value2”] }. You can use the jira-asset-object-type-attribute-list command to retrieve the list of available attributes. |
Optional |
| attributes_json | Provide this field if attributes is not provided. A JSON string of object attributes to be added to the new object. Take a look at https://docs.atlassian.com/assets/REST/10.7.0/#object-createObject for the structure of this field. You can use the jira-asset-object-type-attribute-list command to retrieve the list of available attributes. |
Optional |
Context Output
There is no context output for this command.
jira-asset-object-delete
Deletes an existing object of the specified object ID.
Base Command
jira-asset-object-delete
Input
| Argument Name | Description | Required |
|---|---|---|
| object_id | ID of the object that will be deleted. | Required |
Context Output
There is no context output for this command.
jira-asset-object-get
Retrieves the object with the specified object ID.
Base Command
jira-asset-object-get
Input
| Argument Name | Description | Required |
|---|---|---|
| object_id | ID of the object that will be retrieved. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| JiraAsset.Object.ID | Number | ID of the retrieved object. |
| JiraAsset.Object.Label | String | The value of the attribute that is marked as Label for this object type. |
| JiraAsset.Object.ObjectKey | String | The auto-generated object key. |
| JiraAsset.Object.Created | Date | The date in which this object was created. |
| JiraAsset.Object.Updated | Date | The date in which this object was last updated. |
| JiraAsset.Object.HasAvatar | Boolean | Does this object have an Avatar. |
| JiraAsset.Object.Timestamp | Date | Epoch version of the object’s creation date. |
| JiraAsset.Object.ExtendedInfo.openIssuesExists | Boolean | Does this object appear in any open issues. |
| JiraAsset.Object.ExtendedInfo.attachmentsExists | Boolean | Does this object have any attachments. |
| JiraAsset.Object.Links.self | String | The link to view this object in the Jira web app. |
| JiraAsset.Object.Name | String | The name of the object. |
jira-asset-object-search
Searches for objects of the specified object type.
Base Command
jira-asset-object-search
Input
| Argument Name | Description | Required |
|---|---|---|
| ql_query | An aql query to search objects by. View https://support.atlassian.com/jira-service-management-cloud/docs/use-assets-query-language-aql/ for further details. | Required |
| include_attributes | Whether to include the attributes structure in the response. Possible values are: true, false. | Optional |
| page | Page number. | Optional |
| page_size | Use this argument or limit, but not both. Size of the page. Defaults to 50. | Optional |
| limit | Use this argument or a combination of page and page_size, but not both. Limit the number of entries returned by the command. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| JiraAsset.Object.ID | Number | ID of the retrieved object. |
| JiraAsset.Object.Label | String | The value of the attribute that is marked as Label for this object type. |
| JiraAsset.Object.ObjectKey | String | The auto-generated object key. |
| JiraAsset.Object.Created | Date | The date in which this object was created. |
| JiraAsset.Object.Updated | Date | The date in which this object was last updated. |
| JiraAsset.Object.HasAvatar | Boolean | Does this object have an Avatar. |
| JiraAsset.Object.Timestamp | Date | Epoch version of the object’s creation date. |
| JiraAsset.Object.Attributes.id | Number | The ID of the attribute. |
| JiraAsset.Object.Attributes.objectTypeAttributeId | Number | The ID of the attribute, not relative to the object type. |
| JiraAsset.Object.Attributes.objectAttributeValues.value | String | The value of the specific attribute for this object. |
| JiraAsset.Object.Attributes.objectAttributeValues.referencedType | Boolean | Whether this attribute is referenced by other types. |
| JiraAsset.Object.Attributes.objectAttributeValues.displayValue | String | The display value for this attribute. |
| JiraAsset.Object.Attributes.objectAttributeValues.searchValue | String | The search value for this attribute. |
| JiraAsset.Object.Attributes.objectId | Number | The ID of the object this attribute is assigned to. |
| JiraAsset.Object.Links.self | String | The link to view this object in the Jira web app. |
| JiraAsset.Object.Name | String | The name of the object. |
jira-asset-attribute-json-create
Utility command used to create a JSON file with all attributes of the specified object type. All that is left is to fill in the values of each attribute.
Base Command
jira-asset-attribute-json-create
Input
| Argument Name | Description | Required |
|---|---|---|
| object_type_id | ID of the object type that attributes JSON will be created for. You could use the jira-asset-object-type-list command to find object types. | Required |
| is_editable | Whether to fetch only editable attributes. Possible values are: true, false. | Optional |
| is_required | Whether to fetch only required attributes. Possible values are: true, false. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| InfoFile.Name | string | File name. |
| InfoFile.EntryID | string | The entry ID of the report. |
| InfoFile.Size | number | File size. |
| InfoFile.Type | string | File type, e.g., “PE”. |
| InfoFile.Info | string | Basic information of the file. |
jira-asset-comment-create
Creates a comment for the specified object.
Base Command
jira-asset-comment-create
Input
| Argument Name | Description | Required |
|---|---|---|
| object_id | ID of the object that the comment will be added to. You could use the jira-asset-object-search command to find objects. | Required |
| comment | Body of the comment that will be created. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| JiraAsset.Comment.Created | Date | The date in which this comment was created. |
| JiraAsset.Comment.Updated | Date | The date in which this object was last updated. |
| JiraAsset.Comment.Id | Number | The ID of the newly created comment. |
| JiraAsset.Comment.Actor.AvatarUrl | String | URL to the avatar of the author of the comment. |
| JiraAsset.Comment.Actor.DisplayName | String | Display name of the author of the comment. |
| JiraAsset.Comment.Actor.Name | String | Name of the author of the comment. |
| JiraAsset.Comment.Actor.Key | String | Key of the author of the comment. |
| JiraAsset.Comment.Actor.RenderedLink | String | A link to the author’s Jira profile. |
| JiraAsset.Comment.Actor.IsDeleted | Boolean | Is the author of the comment deleted. |
| JiraAsset.Comment.Role | Number | The role of the author of the comment. |
| JiraAsset.Comment.Comment | String | The body of the comment that was newly added. |
| JiraAsset.Comment.CommentOutput | String | The body of the comment that was newly added. |
| JiraAsset.Comment.ObjectId | Number | The ID of the object the new comment was added to. |
| JiraAsset.Comment.CanEdit | Boolean | Whether the comment is editable. |
| JiraAsset.Comment.CanDelete | Boolean | Whether the comment can be deleted. |
jira-asset-connected-ticket-list
Returns a list of all connected tickets for the specified object.
Base Command
jira-asset-connected-ticket-list
Input
| Argument Name | Description | Required |
|---|---|---|
| object_id | ID of the object for which connected tickets will be fetched. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| JiraAsset.ConnectedTicket.Tickets.Key | String | Key for the connected ticket. |
| JiraAsset.ConnectedTicket.Tickets.ID | Number | The ID of the connected ticket. |
| JiraAsset.ConnectedTicket.Tickets.Reporter | String | The user who reported the connected ticket. |
| JiraAsset.ConnectedTicket.Tickets.Created | Date | The date in which this connected ticket was created. |
| JiraAsset.ConnectedTicket.Tickets.Updated | Date | The date in which this connected ticket was last updated. |
| JiraAsset.ConnectedTicket.Tickets.Title | String | Title of the connected ticket. |
| JiraAsset.ConnectedTicket.Tickets.Status.Name | String | The name of the status of the connected ticket. |
| JiraAsset.ConnectedTicket.Tickets.Status.Colorname | String | The color of the connected tikcet. |
| JiraAsset.ConnectedTicket.Tickets.Type.Name | String | The type of the connected ticket. |
| JiraAsset.ConnectedTicket.Tickets.Type.Description | String | The description of the type of the connected ticket. |
| JiraAsset.ConnectedTicket.Tickets.Type.Iconurl | String | The URL to the icon representing the type of the connected ticket. |
| JiraAsset.ConnectedTicket.Tickets.Priority.Name | String | Priority of the connected ticket. |
| JiraAsset.ConnectedTicket.Tickets.Priority.Iconurl | String | The URL to the icon representing the priority of the connected ticket. |
| JiraAsset.ConnectedTicket.Allticketsquery | String | The query used to fetch the connected tickets. |
jira-asset-comment-list
Returns a list of comments for the specified object.
Base Command
jira-asset-comment-list
Input
| Argument Name | Description | Required |
|---|---|---|
| object_id | ID of the object that comments will be retrieved for. You could use the jira-asset-object-search command to find objects. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| JiraAsset.Comment.Created | Date | The date in which this comment was created. |
| JiraAsset.Comment.Updated | Date | The date in which this object was last updated. |
| JiraAsset.Comment.ID | Number | The ID of the newly created comment. |
| JiraAsset.Comment.Role | Number | The role of the author of the comment. |
| JiraAsset.Comment.Comment | String | The body of the comment that was newly added. |
| JiraAsset.Comment.CommentOutput | String | The body of the comment that was newly added. |
| JiraAsset.Comment.ObjectId | Number | The ID of the object the new comment was added to. |
| JiraAsset.Comment.CanEdit | Boolean | Whether the comment is editable. |
| JiraAsset.Comment.CanDelete | unknown | Whether the comment can be deleted. |
jira-asset-attachment-add
Uploads a file attachment to the specified object.
Base Command
jira-asset-attachment-add
Input
| Argument Name | Description | Required |
|---|---|---|
| object_id | ID of the object that the file will be attached to. | Required |
| entry_id | ID of the file to upload. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| JiraAsset.Attachment.ID | Number | ID of the attachment. |
| JiraAsset.Attachment.Author | String | Author of the attachment. |
| JiraAsset.Attachment.MimeType | String | The file’s MIME type. |
| JiraAsset.Attachment.Filename | String | The file’s name. |
| JiraAsset.Attachment.Filesize | String | The size of the file. |
| JiraAsset.Attachment.Created | Date | The date in which this attachment was created. |
| JiraAsset.Attachment.Comment | String | The comment attached to the attachment, if there is one. |
| JiraAsset.Attachment.CommentOutput | String | The comment output of the attachment, if there is one. |
| JiraAsset.Attachment.Url | String | URL to the attachment. |
jira-asset-attachment-list
Returns a list of attachments for the specified object. You can also download the files in a zipped format.
Base Command
jira-asset-attachment-list
Input
| Argument Name | Description | Required |
|---|---|---|
| object_id | ID of the object that attachments will be fetched for. | Required |
| download_file | Whether to download the attachments for this object in a zipped format. Possible values are: true, false. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| JiraAsset.Attachment.ID | Number | ID of the attachment. |
| JiraAsset.Attachment.Author | String | Author of the attachment. |
| JiraAsset.Attachment.MimeType | String | The file’s MIME type. |
| JiraAsset.Attachment.Filename | String | The file’s name. |
| JiraAsset.Attachment.Filesize | String | The size of the file. |
| JiraAsset.Attachment.Created | Date | The date in which this attachment was created. |
| JiraAsset.Attachment.Comment | String | The comment attached to the attachment, if there is one. |
| JiraAsset.Attachment.CommentOutput | String | The comment output of the attachment, if there is one. |
| JiraAsset.Attachment.Url | String | URL to the attachment. |
jira-asset-attachment-remove
Removes an attachment from a specific object.
Base Command
jira-asset-attachment-remove
Input
| Argument Name | Description | Required |
|---|---|---|
| id | ID of the attachment that will be deleted. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| JiraAsset.Attachment.ID | Number | ID of the attachment that was deleted. |
| JiraAsset.Attachment.Author | String | Author of the attachment. |
| JiraAsset.Attachment.MimeType | String | The file’s MIME type. |
| JiraAsset.Attachment.Filename | String | The file’s name. |
| JiraAsset.Attachment.Filesize | String | The size of the file. |
| JiraAsset.Attachment.Created | Date | The date in which this attachment was created. |
| JiraAsset.Attachment.Comment | String | The comment attached to the attachment, if there is one. |
| JiraAsset.Attachment.CommentOutput | String | The comment output of the attachment, if there is one. |
| JiraAsset.Attachment.Url | String | URL to the attachment. |
Configuration parameters
url— Server URL (required)credentials— (required)insecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (15)
-
jira-asset-attachment-addUploads a file attachment to the specified object.
-
jira-asset-attachment-listReturns a list of attachments for the specified object. You can also download the files in a zipped format.
-
jira-asset-attachment-removeRemoves an attachment from a specific object.
-
jira-asset-attribute-json-createUtility command used to create a JSON file with all attributes of the specified object type. All that is left is to fill in the values of each attribute.
-
jira-asset-comment-createCreates a comment for the specified object.
-
jira-asset-comment-listReturns a list of comments for the specified object.
-
jira-asset-connected-ticket-listReturns a list of all connected tickets for the specified object.
-
jira-asset-object-createCreate a new object of the specified object type ID. Either attributes or attributes_json must be provided.
-
jira-asset-object-deleteDeletes an existing object of the specified object ID.
-
jira-asset-object-getRetrieves the object with the specified object ID.
-
jira-asset-object-schema-listList all object schemas.
-
jira-asset-object-searchSearches for objects of the specified object type.
-
jira-asset-object-type-attribute-listList all attributes for an object type.
-
jira-asset-object-type-listList all object types.
-
jira-asset-object-updateUpdates an existing object of specified object type ID. Either attributes or attributes_json must be provided.
import io import json import os import zipfile from typing import Any import demistomock as demisto # noqa: F401 import urllib3 from CommonServerPython import * # noqa: F401 from CommonServerUserPython import * # noqa # Disable insecure warnings urllib3.disable_warnings() """ CONSTANTS """ DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ" # ISO8601 format with UTC, default in XSOAR ON_PREM_URL_STRUCTURE = "{}/rest/assets/1.0/" INTEGRATION_OUTPUTS_BASE_PATH = "JiraAsset" """ CLIENT CLASS """ class Client(BaseClient): def __init__(self, base_url: str, api_key: str, verify: bool, proxy: bool): headers = {"Authorization": f"Bearer {api_key}", "Accept": "application/json"} super().__init__(base_url=base_url, verify=verify, proxy=proxy, headers=headers, ok_codes=(200, 201, 204, 404)) def get_schema_list(self): return self._http_request(method="GET", url_suffix="/objectschema/list") def get_object_type_list(self, schema_id: str, exclude): url_suffix = f"objectschema/{schema_id}/objecttypes/flat" # build request params params = {"exclude": exclude} if exclude else {} return self._http_request(method="GET", url_suffix=url_suffix, params=params) def get_object_type_attributes( self, object_type_id: str, is_editable: bool = False, order_by_name: bool = False, query: str = None, include_value_exist: bool = False, exclude_parent_attributes: bool = False, include_children: bool = False, order_by_required: bool = False, ): url_suffix = f"objecttype/{object_type_id}/attributes" # build request params params = { "onlyValueEditable": is_editable, "orderByName": order_by_name, "query": query, "includeValueExist": include_value_exist, "excludeParentAttributes": exclude_parent_attributes, "includeChildren": include_children, "orderByRequired": order_by_required, } return self._http_request(method="GET", url_suffix=url_suffix, params=params) def get_object(self, object_id): """ hr_headers = ['ID', 'Label', 'Type', 'Created'] readable_output = get_object_readable_outputs([res]) outputs = {k: v for k, v in res.items() if k != 'objectType' and k != 'avatar'} """ res = self._http_request(method="GET", url_suffix=f"/object/{object_id}", resp_type="response") if res.status_code == 404: return None return res.json() def search_objects(self, ql_query: str, include_attributes: bool, page: int, page_size: int, limit: int = None): params = { "qlQuery": ql_query, "includeAttributes": include_attributes, "page": page, "resultsPerPage": limit if limit else page_size, } return self._http_request(method="GET", url_suffix="/aql/objects", params=params) def get_comment_list(self, object_id: str): return self._http_request(method="GET", url_suffix=f"/comment/object/{object_id}") def get_object_connected_tickets(self, object_id: str): return self._http_request(method="GET", url_suffix=f"/objectconnectedtickets/{object_id}/tickets") def get_object_attachment_list(self, object_id): res = self._http_request(method="GET", url_suffix=f"/attachments/object/{object_id}", resp_type="response") if res.status_code == 404: return None return res.json() def get_file(self, file_url): return self._http_request(method="GET", full_url=file_url, resp_type="response") def create_object(self, json_data): return self._http_request(method="POST", url_suffix="/object/create", json_data=json_data) def create_comment(self, object_id, comment): url_suffix = "/comment/create" json_data = {"objectId": object_id, "comment": comment} return self._http_request(method="POST", url_suffix=url_suffix, json_data=json_data) def send_file(self, object_id, file_path): return self._http_request( method="POST", url_suffix=f"/attachments/object/{object_id}", files={"file": (file_path, open(file_path, "rb"))} ) def update_object(self, object_id: str, json_data: Dict[str, Any]): return self._http_request(method="PUT", url_suffix=f"/object/{object_id}", json_data=json_data) def delete_object(self, object_id: str): res = self._http_request(method="DELETE", url_suffix=f"/object/{object_id}", resp_type="response") if res.status_code == 404: return None return res.json() def remove_file(self, attachment_id): return self._http_request(method="DELETE", url_suffix=f"/attachments/{attachment_id}") """ HELPER FUNCTIONS """ def pascal_case(s: str) -> str: """Convert a string to PascalCase.""" words = s.split("_") return "".join(word[:1].upper() + word[1:] for word in words) def convert_keys_to_pascal(objects: List[dict[str, Any]], key_mapping: Optional[dict[str, str]] = None) -> List[dict[str, Any]]: """ Convert keys of objects in a list to PascalCase, with optional key mapping. Args: objects (list): List of dictionaries (objects) whose keys need conversion. key_mapping (dict): Dictionary containing original keys and expected output keys. Returns: list: List of dictionaries with keys converted to PascalCase or as per key_mapping. """ if not key_mapping: key_mapping = {} converted_objects = [] for obj in objects: converted_obj = {} for key, value in obj.items(): if key in key_mapping: new_key = key_mapping[key] else: new_key = pascal_case(key) converted_obj[new_key] = value converted_objects.append(converted_obj) return converted_objects def get_object_attribute_string_type(attribute: Dict[str, Any]) -> str: match attribute["type"]: case 0: return attribute["defaultType"]["name"] case 1: return "Object Reference" case 2: return "User" case 4: return "Group" case 6: return "Project" case 7: return "Status" case 8: return "Bitbucket Repository" return "Default" def get_object_readable_outputs(objects: List[Dict[str, Any]]) -> List[Dict[str, Any]]: pascal_objects = convert_keys_to_pascal(objects, {"id": "ID"}) readable_outputs = [] for obj in pascal_objects: obj_type = obj["ObjectType"]["name"] output = {k: v for k, v in obj.items() if k not in ("ObjectType", "Avatar")} readable_output = {**output, "Type": obj_type} readable_outputs.append(readable_output) return readable_outputs def clean_object_attributes(attributes: List[Dict[str, Any]]) -> List[Dict[str, Any]]: string_typed_attributes = [{**attribute, "type": get_object_attribute_string_type(attribute)} for attribute in attributes] return [{k: v for k, v in attribute.items() if k != "objectType"} for attribute in string_typed_attributes] def convert_attributes(attributes: Dict[str, List[str]]) -> List[Dict[str, Any]]: result = [] for attribute_id, values in attributes.items(): values = argToList(values) attribute_dict = {"objectTypeAttributeId": attribute_id, "objectAttributeValues": [{"value": value} for value in values]} result.append(attribute_dict) return result def get_attributes_json_data( object_type_id: str, attributes: dict[str, Any] = None, attributes_json: str = None ) -> Dict[str, Any]: if not attributes and not attributes_json: raise ValueError("Either attributes or attributes_json must be provided.") elif attributes and attributes_json: raise ValueError("Only one of attributes or attributes_json must be provided.") if attributes: converted_attributes = convert_attributes(attributes) else: # cast is necessary for pre-commit. mypy isn't smart enough to know that by that point, attributes_json must be defined converted_attributes = json.loads(str(attributes_json)).get("attributes") return {"objectTypeId": object_type_id, "attributes": converted_attributes} def parse_object_results(res: Dict[str, Any]) -> Dict[str, Any]: outputs = [{k: v for k, v in res.items() if k != "objectType"}] object_id = res.get("id") return {"outputs": outputs, "objectId": object_id} """ COMMAND FUNCTIONS """ def test_module(client: Client) -> str: """Tests API connectivity and authentication' Returning 'ok' indicates that the integration works like it is supposed to. Connection to the service is successful. Raises exceptions if something goes wrong. :type client: ``Client`` :param Client: client to use :return: 'ok' if test passed, anything else will fail the test. :rtype: ``str`` """ try: client.get_schema_list() return "ok" except DemistoException as e: if "Forbidden" in str(e) or "Authorization" in str(e): # TODO: make sure you capture authentication errors message = "Authorization Error: make sure API Key is correctly set" else: raise e return message def jira_asset_object_schema_list_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Retrieves a list of Jira asset object schemas with an option to limit the number of results returned. :param client: Client object for performing API requests. :param args: A dictionary of command arguments: - 'limit': The maximum number of object schemas to return. Defaults to 50. - 'all_results': A boolean indicating whether to return all results or to respect the limit. Defaults to False. :return: A CommandResults object containing the list of object schemas as output, and a human-readable markdown table. """ limit = args.get("limit", 50) all_results = argToBoolean(args.get("all_results", False)) res = client.get_schema_list() object_schemas = res.get("objectschemas", []) key_mapping = {"id": "ID", "objectSchemaKey": "Key"} if not all_results: limit = int(limit) object_schemas = object_schemas[:limit] readable_outputs = convert_keys_to_pascal(object_schemas, key_mapping) hr_headers = ["ID", "Name", "Key", "Status", "Description", "Created"] return CommandResults( outputs_prefix=f"{INTEGRATION_OUTPUTS_BASE_PATH}.Schema", outputs_key_field="ID", outputs=object_schemas, readable_output=tableToMarkdown("Object Schemas", readable_outputs, headers=hr_headers, removeNull=True), ) def jira_asset_object_type_list_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Retrieves a list of Jira asset object types based on provided arguments. :param client: Client object for performing API requests. :param args: A dictionary of command arguments: - 'schema_id': The ID of the schema to retrieve object types from. - 'exclude': A string to specify object types that should be excluded from the results. - 'limit': The maximum number of object types to return. Defaults to 50. - 'all_results': A boolean indicating whether to return all results or to respect the limit. Defaults to False. :return: A CommandResults object containing the list of object types as output, and a human-readable markdown table. """ schema_id = args.get("schema_id", "") exclude = args.get("exclude") limit = args.get("limit", 50) all_results = argToBoolean(args.get("all_results", False)) if not schema_id: raise ValueError("schema_id is a required argument") # build outputs res = client.get_object_type_list(schema_id, exclude) outputs = [{k: v for k, v in ot.items() if k != "icon"} for ot in res] if not all_results: limit = int(limit) outputs = outputs[:limit] readable_outputs = convert_keys_to_pascal(list(outputs), {"id": "ID", "parentObjectTypeId": "ParentTypeID"}) # build readable outputs hr_headers = ["ID", "Name", "ParentTypeID", "AbstractObjectType"] return CommandResults( outputs_prefix=f"{INTEGRATION_OUTPUTS_BASE_PATH}.ObjectType", outputs_key_field="ID", outputs=outputs, readable_output=tableToMarkdown("Object Types", readable_outputs, headers=hr_headers, removeNull=True), ) def jira_asset_object_type_attribute_list_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Retrieves a list of attributes for a specific Jira asset object type with various filtering and sorting options. :param client: Client object for performing API requests. :param args: A dictionary of command arguments: - 'object_type_id': The ID of the object type to retrieve attributes for. - 'limit': The maximum number of attributes to return. Defaults to 50. - 'all_results': A boolean indicating whether to return all results or to respect the limit. Defaults to False. - Additional boolean arguments for filtering and sorting: * 'order_by_name' * 'include_value_exist' * 'exclude_parent_attributes' * 'include_children' * 'order_by_required' :return: A CommandResults object containing the list of attributes as output, and a human-readable markdown table. """ object_type_id = args.get("object_type_id", "") limit = args.get("limit", 50) all_results = args.get("all_results", False) if not object_type_id: raise ValueError("object_type_id is a required argument") # build outputs res = client.get_object_type_attributes( object_type_id=object_type_id, order_by_name=argToBoolean(args.get("order_by_name", False)), query=args.get("query"), include_value_exist=argToBoolean(args.get("include_value_exist", False)), exclude_parent_attributes=argToBoolean(args.get("exclude_parent_attributes", False)), include_children=argToBoolean(args.get("include_children", False)), order_by_required=argToBoolean(args.get("order_by_required", False)), ) outputs = clean_object_attributes(list(res)) if not all_results: limit = int(limit) outputs = outputs[:limit] hr_headers = ["ID", "Name", "Type", "Description", "MinimumCardinality", "Editable"] readable_outputs = convert_keys_to_pascal(list(res), {"id": "ID"}) return CommandResults( outputs_prefix=f"{INTEGRATION_OUTPUTS_BASE_PATH}.Attribute", outputs_key_field="ID", outputs=outputs, readable_output=tableToMarkdown("Object Types", readable_outputs, headers=hr_headers, removeNull=True), ) def jira_asset_object_create_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Creates a new Jira asset object. :param client: Client object for performing API requests. :param args: A dictionary of command arguments: - 'object_type_id': The ID of the object type to create the object for. - 'attributes': A list of attribute names and values to set for the object. - 'attributes_json': A JSON string of attributes to set for the object. :return: A CommandResults object containing the created object as output, and a human-readable markdown table. """ object_type_id = args.get("object_type_id", "") attributes = args.get("attributes") attributes_json = args.get("attributes_json") if not object_type_id: raise ValueError("object_type_id is a required argument") json_data = get_attributes_json_data(object_type_id, attributes, attributes_json) res = client.create_object(json_data) outputs, object_id = parse_object_results(res).values() return CommandResults( outputs=outputs, outputs_prefix=f"{INTEGRATION_OUTPUTS_BASE_PATH}.Object", readable_output=f"Object created successfully with ID: {object_id}", ) def jira_asset_object_update_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Updates an existing Jira asset object. :param client: Client object for performing API requests. :param args: A dictionary of command arguments: - 'object_id': The ID of the object to update. - 'attributes': A list of attribute names and values to set for the object. - 'attributes_json': A JSON string of attributes to set for the object. :return: A CommandResults object containing the updated object as output, and a human-readable markdown table. """ attributes = args.get("attributes") attributes_json = args.get("attributes_json") object_id = args.get("object_id", "") if not object_id: raise ValueError("object_id is a required argument") jira_object = client.get_object(object_id) if not jira_object: return CommandResults(readable_output=f"Object with id: {object_id} does not exist") object_type_id = jira_object.get("objectType").get("id") json_data = get_attributes_json_data(object_type_id, attributes, attributes_json) res = client.update_object(object_id, json_data) _, object_id = parse_object_results(res).values() return CommandResults(readable_output=f"Object {object_id} updated successfully") def jira_asset_object_delete_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Deletes an existing Jira asset object. :param client: Client object for performing API requests. :param args: A dictionary of command arguments: - 'object_id': The ID of the object to delete. :return: A CommandResults object containing a human-readable message. """ object_id = args.get("object_id", "") if not object_id: raise ValueError("object_id is a required argument") res = client.delete_object(object_id) if not res: return CommandResults(readable_output=f"Object with id: {object_id} does not exist") return CommandResults(readable_output=f"Object with id: {object_id} was deleted successfully") def jira_asset_object_get_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Retrieves an existing Jira asset object. :param client: Client object for performing API requests. :param args: A dictionary of command arguments: - 'object_id': The ID of the object to retrieve. :return: A CommandResults object containing the object as output, and a human-readable markdown table. """ object_id = args.get("object_id", "") if not object_id: raise ValueError("object_id is a required argument") res = client.get_object(object_id) if not res: return CommandResults(readable_output=f"Object with id: {object_id} does not exist") hr_headers = ["ID", "Label", "Type", "Created"] readable_output = get_object_readable_outputs([res]) outputs = {k: v for k, v in res.items() if k != "objectType" and k != "avatar"} return CommandResults( outputs_prefix=f"{INTEGRATION_OUTPUTS_BASE_PATH}.Object", outputs_key_field="ID", outputs=outputs, readable_output=tableToMarkdown("Object", readable_output, headers=hr_headers, removeNull=True), ) def jira_asset_object_search_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Searches for Jira asset objects. :param client: Client object for performing API requests. :param args: A dictionary of command arguments: - 'ql_query': The query string to search for. - 'include_attributes': Whether to include the object's attributes in the response. - 'page': The page number to retrieve. - 'page_size': The number of objects to retrieve per page. - 'limit': The maximum number of objects to retrieve. :return: A CommandResults object containing the objects as output, and a human-readable markdown table. """ # build request params ql_query = args.get("ql_query", "") include_attributes = argToBoolean(args.get("include_attributes", False)) page = int(args.get("page", 1)) page_size = int(args.get("page_size", 50)) limit = args.get("limit") if not ql_query: raise ValueError("ql_query is a required argument") # build outputs res = client.search_objects(ql_query, include_attributes, page, page_size, limit) objects = res.get("objectEntries", []) if not objects: return CommandResults(readable_output="No objects found.") hr_headers = ["ID", "Label", "Type", "ObjectKey"] readable_output = get_object_readable_outputs(objects) outputs = [{k: v for k, v in obj.items() if k != "objectType" and k != "avatar"} for obj in objects] return CommandResults( outputs_prefix=f"{INTEGRATION_OUTPUTS_BASE_PATH}.Object", outputs_key_field="ID", outputs=outputs, readable_output=tableToMarkdown("Object", readable_output, headers=hr_headers, removeNull=True), ) def jira_asset_attribute_json_create_command(client: Client, args: Dict[str, Any]) -> tuple[dict, CommandResults]: """ Creates a Jira asset attribute JSON object. :param client: Client object for performing API requests. :param args: A dictionary of command arguments: - 'object_type_id': The ID of the object type to create the attribute JSON for. - 'is_required': Whether to include only required attributes in the response. - 'is_editable': Whether to include only editable attributes in the response. :return: A CommandResults object containing the attribute JSON as a file entry, and a human-readable json string. """ # build request params object_type_id = args.get("object_type_id", "") is_editable = args.get("is_editable", False) if not object_type_id: raise ValueError("object_type_id is a required argument") # build outputs res = client.get_object_type_attributes(object_type_id=object_type_id, is_editable=is_editable) if args.get("is_required"): res = [attribute for attribute in res if int(attribute.get("minimumCardinality")) > 0] outputs = { "attributes": [ {"objectTypeAttributeId": attribute.get("id"), "objectAttributeValues": [{"value": ""}]} for attribute in res ] } hr_command_results = CommandResults(readable_output=json.dumps(outputs)) file_entry = fileResult(filename="attributes.json", data=json.dumps(outputs, indent=2), file_type=EntryType.ENTRY_INFO_FILE) return file_entry, hr_command_results def jira_asset_comment_create_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Creates a Jira asset comment. :param client: Client object for performing API requests. :param args: A dictionary of command arguments: - 'object_id': The ID of the object to create the comment for. - 'comment': The comment to add to the object. :return: A CommandResults object containing the comment as output, and a confirmation message. """ object_id = args.get("object_id") comment = args.get("comment") res = client.create_comment(object_id, comment) return CommandResults( outputs_prefix=f"{INTEGRATION_OUTPUTS_BASE_PATH}.Comment", outputs_key_field="ID", outputs=res, readable_output=f"Comment was added successfully to object with id: {object_id}", ) def jira_asset_comment_list_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Lists Jira asset comments. :param client: Client object for performing API requests. :param args: A dictionary of command arguments: - 'object_id': The ID of the object to retrieve comments for. :return: A CommandResults object containing the comments as output, and a human-readable markdown table. """ object_id = args.get("object_id", "") if not object_id: raise ValueError("object_id is a required argument") res = client.get_comment_list(object_id) readable_outputs = convert_keys_to_pascal(list(res), {"id": "ID"}) outputs = [{k: v for k, v in output.items() if k != "actor"} for output in res] hr_headers = ["ID", "Comment"] return CommandResults( outputs_prefix=f"{INTEGRATION_OUTPUTS_BASE_PATH}.Comment", outputs_key_field="ID", outputs=outputs, readable_output=tableToMarkdown("Comments", readable_outputs, headers=hr_headers, removeNull=True), ) def jira_asset_connected_ticket_list_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Lists Jira asset connected tickets. :param client: Client object for performing API requests. :param args: A dictionary of command arguments: - 'object_id': The ID of the object to retrieve connected tickets for. :return: A CommandResults object containing the connected tickets as output, and a human-readable markdown table. """ object_id = args.get("object_id", "") if not object_id: raise ValueError("object_id is a required argument") res = client.get_object_connected_tickets(object_id) outputs = list(res.get("tickets")) hr_headers = ["ID", "Title", "Status", "Type"] readable_output = [ { "Status": output.get("status", {}).get("name"), "Type": output.get("type", {}).get("name"), "Title": output.get("title", {}), } for output in outputs ] return CommandResults( outputs_prefix=f"{INTEGRATION_OUTPUTS_BASE_PATH}.ConnectedTicket", outputs_key_field="ID", outputs=outputs, readable_output=tableToMarkdown("Connected Tickets", readable_output, headers=hr_headers, removeNull=True), ) def jira_asset_attachment_add_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Adds a Jira asset attachment to a specific object. :param client: Client object for performing API requests. :param args: A dictionary of command arguments: - 'object_id': The ID of the object to add the attachment to. - 'entry_id': The entry ID of the file to add as an attachment. :return: A CommandResults object containing the attachment as output, and a confirmation message. """ object_id = args.get("object_id") entry_id = args.get("entry_id") file_path = demisto.getFilePath(entry_id) demisto.debug(f"File path: {file_path}") res = client.send_file(object_id=object_id, file_path=file_path.get("path")) return CommandResults( outputs_prefix=f"{INTEGRATION_OUTPUTS_BASE_PATH}.Attachment", outputs_key_field="ID", outputs=res, readable_output=f"Attachment was added successfully to object with id: {object_id}", ) def jira_asset_attachment_list_command(client: Client, args: dict[str, Any]) -> List[CommandResults | dict] | CommandResults: """ Lists Jira asset attachments for a specific object. :param client: Client object for performing API requests. :param args: A dictionary of command arguments: - 'object_id': The ID of the object to retrieve attachments for. - 'download_file': Whether to download the attachments. :return: A CommandResults object containing the attachments as output, and a human-readable markdown table if download_file is False. If download_file is True, a list of CommandResults objects containing the attachments as output, and a fileResult object. """ object_id = args.get("object_id") download_file = argToBoolean(args.get("download_file", False)) res = client.get_object_attachment_list(f"/attachments/object/{object_id}") if not res: return CommandResults(readable_output="No attachments found.") readable_outputs = convert_keys_to_pascal(list(res), {"id": "ID"}) hr_header = ["ID", "Filename", "Filesize", "Comment"] command_results = CommandResults( outputs_prefix=f"{INTEGRATION_OUTPUTS_BASE_PATH}.Attachment", outputs_key_field="ID", outputs=res, readable_output=tableToMarkdown("Attachments", readable_outputs, headers=hr_header, removeNull=True), ) if not download_file: return command_results files = [] for attachment in res: attachment_url = attachment.get("url") file_response = client.get_file(attachment_url) file_name = attachment_url.split("/")[-1] i = 1 while file_name in files: name, ext = file_name.split(".") file_name = f"{name}_{i}.{ext}" i += 1 with open(file_name, "wb") as file: file.write(file_response.content) files.append(file.name) zip_data_buffer = io.BytesIO() with zipfile.ZipFile(zip_data_buffer, "w") as zipf: for curr_file_name in files: zipf.write(curr_file_name) os.remove(curr_file_name) data = zip_data_buffer.getvalue() file_result = fileResult("ObjectAttachments.zip", data, EntryType.ENTRY_INFO_FILE) return [file_result, command_results] def jira_asset_attachment_remove_command(client: Client, args: dict[str, Any]) -> CommandResults: """ Removes a Jira asset attachment. :param client: Client object for performing API requests. :param args: A dictionary of command arguments: - 'id': The ID of the attachment to remove. :return: A CommandResults object containing the attachment as output, and a confirmation message. """ attachment_id = args.get("id") try: res = client.remove_file(attachment_id) except DemistoException as e: if e.res.status_code == 404: return CommandResults(readable_output=f"Attachment with id: {attachment_id} does not exist") else: raise e return CommandResults( outputs_prefix=f"{INTEGRATION_OUTPUTS_BASE_PATH}.Attachment", outputs_key_field="ID", outputs=res, readable_output=f'Attachment with id {res.get("id")} was successfully deleted', ) """ MAIN FUNCTION """ commands = { "jira-asset-object-schema-list": jira_asset_object_schema_list_command, "jira-asset-object-type-list": jira_asset_object_type_list_command, "jira-asset-object-type-attribute-list": jira_asset_object_type_attribute_list_command, "jira-asset-object-create": jira_asset_object_create_command, "jira-asset-object-update": jira_asset_object_update_command, "jira-asset-object-delete": jira_asset_object_delete_command, "jira-asset-object-get": jira_asset_object_get_command, "jira-asset-object-search": jira_asset_object_search_command, "jira-asset-attribute-json-create": jira_asset_attribute_json_create_command, "jira-asset-comment-create": jira_asset_comment_create_command, "jira-asset-comment-list": jira_asset_comment_list_command, "jira-asset-connected-ticket-list": jira_asset_connected_ticket_list_command, "jira-asset-attachment-add": jira_asset_attachment_add_command, "jira-asset-attachment-remove": jira_asset_attachment_remove_command, "jira-asset-attachment-list": jira_asset_attachment_list_command, } def main() -> None: command = demisto.command() args = demisto.args() params = demisto.params() api_key = params.get("credentials", {}).get("password", "") server_url = params.get("url") verify_certificate = not params.get("insecure", False) proxy = params.get("proxy", False) try: base_url = ON_PREM_URL_STRUCTURE.format(server_url) client = Client(base_url=base_url, verify=verify_certificate, proxy=proxy, api_key=api_key) if command == "test-module": # This is the call made when pressing the integration Test button. test_result = test_module(client) return_results(test_result) command_func = commands.get(command) if not command_func: raise NotImplementedError(f"""Command '{command}' is not implemented.""") result = command_func(client, args) return_results(result) except Exception as e: demisto.debug(f"The integration context_data is {get_integration_context()}") return_error(f"Failed to execute {demisto.command()} command.\nError:\n{traceback.format_exc()}\nException is: {e!s}") """ ENTRY POINT """ if __name__ in ("__main__", "__builtin__", "builtins"): main()