diff --git a/atlassian/jira.py b/atlassian/jira.py index 003cbf309..d26e68a18 100644 --- a/atlassian/jira.py +++ b/atlassian/jira.py @@ -1,6 +1,7 @@ # coding=utf-8 import logging import re +import os from warnings import warn from deprecated import deprecated from requests import HTTPError @@ -163,6 +164,18 @@ def get_application_role(self, role_key): Reference: https://docs.atlassian.com/software/jira/docs/api/REST/8.5.0/#api/2/attachment """ + def get_attachments_ids_from_issue(self, issue): + """ + Get attachments IDs from jira issue + :param jira issue key: str + :return: list of integers attachment IDs + """ + issue_id = self.get_issue(issue)["fields"]["attachment"] + list_attachments_id = [] + for attachment in issue_id: + list_attachments_id.append({"filename": attachment["filename"], "attachment_id": attachment["id"]}) + return list_attachments_id + def get_attachment(self, attachment_id): """ Returns the meta-data for an attachment, including the URI of the actual attached file @@ -173,6 +186,44 @@ def get_attachment(self, attachment_id): url = "{base_url}/{attachment_id}".format(base_url=base_url, attachment_id=attachment_id) return self.get(url) + def download_attachments_from_issue(self, issue, path=None, cloud=True): + """ + Downloads all attachments from a Jira issue. + :param issue: The issue-key of the Jira issue + :param path: Path to directory where attachments will be saved. If None, current working directory will be used. + :param cloud: Use True for Jira Cloud, false when using Jira Data Center or Server + :return: A message indicating the result of the download operation. + """ + try: + if path is None: + path = os.getcwd() + issue_id = self.issue(issue, fields="id")["id"] + if cloud: + url = self.url + f"/secure/issueAttachments/{issue_id}.zip" + else: + url = self.url + f"/secure/attachmentzip/{issue_id}.zip" + response = self._session.get(url) + attachment_name = f"{issue_id}_attachments.zip" + file_path = os.path.join(path, attachment_name) + # if Jira issue doesn't have any attachments _session.get request response will return 22 bytes of PKzip format + file_size = sum(len(chunk) for chunk in response.iter_content(8196)) + if file_size == 22: + return "No attachments found on the Jira issue" + if os.path.isfile(file_path): + return "File already exists" + with open(file_path, "wb") as f: + f.write(response.content) + return "Attachments downloaded successfully" + + except FileNotFoundError: + raise FileNotFoundError("Verify if directory path is correct and/or if directory exists") + except PermissionError: + raise PermissionError( + "Directory found, but there is a problem with saving file to this directory. Check directory permissions" + ) + except Exception as e: + raise e + def get_attachment_content(self, attachment_id): """ Returns the content for an attachment diff --git a/docs/jira.rst b/docs/jira.rst index 5e8e3be05..e6c761b5b 100644 --- a/docs/jira.rst +++ b/docs/jira.rst @@ -489,6 +489,12 @@ Attachments actions # Add attachment (IO Object) to issue jira.add_attachment_object(issue_key, attachment) + # Download attachments from the issue + jira.download_attachments_from_issue(issue, path=None, cloud=True): + + # Get list of attachments ids from issue + jira.get_attachments_ids_from_issue(issue_key) + Manage components ----------------- diff --git a/examples/jira/jira_download_attachments.from_issue.py b/examples/jira/jira_download_attachments.from_issue.py new file mode 100644 index 000000000..6dc6f6cdf --- /dev/null +++ b/examples/jira/jira_download_attachments.from_issue.py @@ -0,0 +1,9 @@ +from atlassian import Jira + +jira_cloud = Jira(url="", username="username", password="password") +jira_dc = Jira(url="url", token=">") +path = "/Users/>/PycharmProjects/api_python_atlassian_features/api_python_atlassian_features/atlassian-python-api/attachments" +# JIRA DC using custom directory path +jira_dc.download_attachments_from_issue("TEST-1", path=path, cloud=False) +# Jira cloud. Attachemtns will be saved to same director where script is being executed. +jira_cloud.get_attachments_ids_from_page("SC-1", cloud=True)