Skip to content

Commit ea179f7

Browse files
committed
add get_all_attachment_contents method and update docs
1 parent 63e744f commit ea179f7

2 files changed

Lines changed: 67 additions & 3 deletions

File tree

atlassian/jira.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import logging
33
import os
44
import re
5+
import zipfile
56
from typing import Any, BinaryIO, Dict, List, Optional, Union, cast
67
from warnings import warn
78

@@ -257,7 +258,7 @@ def get_attachment(self, attachment_id: T_id) -> T_resp_json:
257258

258259
def download_issue_attachments(
259260
self,
260-
issue: str,
261+
issue: T_id,
261262
path: Optional[str] = None,
262263
overwrite: bool = False,
263264
stream: bool = False,
@@ -266,6 +267,8 @@ def download_issue_attachments(
266267
) -> Optional[str]:
267268
"""
268269
Downloads all attachments from a Jira issue.
270+
This method downloads zip file compressed from Jira server side, and may fail if total attachment size is too large.
271+
Use `get_all_attachment_contents()` to download individual attachments and zip from client side.
269272
:param issue: The issue-key of the Jira issue
270273
:param path: Path to directory where attachments will be saved. If None, current working directory will be used.
271274
:param overwrite: If True, always download and create new zip file.
@@ -378,6 +381,53 @@ def get_attachment_content(self, attachment_id: T_id) -> bytes:
378381
headers={"Accept": "*/*"},
379382
)
380383

384+
def get_all_attachment_contents(
385+
self,
386+
issue: T_id,
387+
path: Optional[str] = None,
388+
overwrite: bool = False,
389+
compression: int = zipfile.ZIP_STORED,
390+
) -> Optional[str]:
391+
"""
392+
Downloads all attachments from a Jira issue by downloading individual files and creating zip file.
393+
This method is useful when total attachment size is too large for Jira server to compress as single file.
394+
If total attachment size is small enough, using `download_issue_attachments()` may be more efficient.
395+
:param issue: The issue-key of the Jira issue
396+
:param path: Path to directory where attachments will be saved. If None, current working directory will be used.
397+
:param overwrite: If True, always download and create new zip file.
398+
If False (default), download will be skipped when zip file already exists in path.
399+
:param compression: Compression method for zipfile. Should be one of the constants listed in documentation page.
400+
https://docs.python.org/3/library/zipfile.html#zipfile.ZipFile
401+
:return: File path of the zip file if file is existing or download is successful. None if attachment does not exist.
402+
"""
403+
try:
404+
if path is None:
405+
path = os.getcwd()
406+
issue_id = self.issue(issue, fields="id")["id"]
407+
attachment_name = f"{issue_id}_attachments.zip"
408+
file_path = os.path.join(path, attachment_name)
409+
if not overwrite and os.path.isfile(file_path):
410+
return file_path
411+
412+
attachments_metadata = self.get_attachments_ids_from_issue(issue)
413+
if not attachments_metadata:
414+
return None
415+
416+
with zipfile.ZipFile(file_path, "w", compression=compression) as file:
417+
for meta in attachments_metadata:
418+
file.writestr(meta["filename"], self.get_attachment_content(meta["attachment_id"]))
419+
420+
return file_path
421+
422+
except FileNotFoundError:
423+
raise FileNotFoundError("Verify if directory path is correct and/or if directory exists")
424+
except PermissionError:
425+
raise PermissionError(
426+
"Directory found, but there is a problem with saving file to this directory. Check directory permissions"
427+
)
428+
except Exception as e:
429+
raise e
430+
381431
def remove_attachment(self, attachment_id: T_id) -> T_resp_json:
382432
"""
383433
Remove an attachment from an issue

docs/jira.rst

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -577,10 +577,24 @@ Attachments actions
577577
# Add attachment (IO Object) to issue
578578
jira.add_attachment_object(issue_key, attachment)
579579
580+
# Gets the binary raw data of single attachment in bytes.
581+
jira.get_attachment_content(attachment_id)
582+
580583
# Download attachments from the issue
581-
jira.download_attachments_from_issue(issue, path=None, cloud=True):
584+
# For both methods, if path is None, current working directory is used.
585+
# zip file name is in following format: "<issue id>_attachments.zip"
586+
587+
# This method downloads zip file compressed from Jira server side.
588+
# Best when total attachment size is less than 1 GB.
589+
# Returns a message indicating the result of the download operation.
590+
jira.download_issue_attachments(issue_key, path=None, overwrite=False)
591+
592+
# This method downloads individual files and compresses zip file locally.
593+
# Best when total attachment size is greater than 1 GB.
594+
# Returns the file path of created zip file.
595+
jira.get_all_attachment_contents(issue_key, path=None, overwrite=False)
582596
583-
# Get list of attachments ids from issue
597+
# Get list of attachment names and ids from issue
584598
jira.get_attachments_ids_from_issue(issue_key)
585599
586600
Manage components

0 commit comments

Comments
 (0)