본문 바로가기

메모&낙서장

Deleting attachments within JIRA

(This post has been translated into English with the help of Copilot.)

Having used Jira for many years as a collaboration tool for work, the most concerning issue has been the indiscriminate registration and neglect of attachments, including personal information, without being deleted. Considering the nature of the system operated for the purpose of receiving main work requests and managing the process and results through comments, I did not think that consistently instructing to not upload attachments to Jira but instead exchange them via separate emails, or deleting all attachments within tickets immediately after the work is completed, was desirable from the perspective of effectiveness and work efficiency.

 

Considering the convenience for users to upload attachments to Jira and having the deletion of attachments handled by the Jira management department instead of the users, I thought it would be helpful to introduce the following process.

※ Note: Atlassian supports the detection and deletion of credential, financial data, and identity data through a subscription program called 'Atlassian Guard.' However, since it is a paid service, it has been excluded from consideration.(https://www.atlassian.com/software/guard/pricing)

 

- Files that do not contain personal information are retained for work history management, and only attachments suspected of containing personal information are deleted based on established criteria (e.g., deletion of attachments in tickets where the ticket status is 'closed' and more than 10 days have passed since the ticket was created) on a regular basis.

 

- To minimize user complaints and work inconveniences, inform users in advance via comments that attachments are scheduled for deletion.

 

There are a few things needed to implement the above process.

 

1. Designate tickets that can include data containing personal information as part of the work request or results, and establish a way to distinguish these tickets from other tickets. ( Needed to selectively identify attachments that contain personal information. )

 > Using Project, Issue Type, Component, Label, or Radio Button, assign distinguishing characteristics to tickets that do not contain personal information.

 

2. Identify the Issue Key of tickets handling attachments containing personal information through JQL search.

example) project = "IT Request" AND type = data AND "PID[radio buttons]" = YES ORDER BY created DESC

 

3. Download the Issue Key in csv format using Jira's search results Export function.

 

4. Select only tickets accessible via the Issue Key that contain attachments from among the Jira tickets.

 > Writing Python code using Copilot to avoid the hassle of manually clicking through screens to check for the existence of attachments.  

 

<Description>

- config.json : A file containing configuration information such as the Jira URL, account, and token.

※ token path : Jira > Click on the profile image at the top right corner > Manage Account > Security > API tokens

- issue_keys.txt : A file containing the issue key of the attachments to be deleted (e.g., ITREQ-111).

- attachement_check.py : A code that checks whether there are attachments for each issue key listed in issue_keys.txt.

 

□ config.json

{ "jira_url": "Input your jira URL", "username": "Input your account", "api_token": "Input your token" }

 

□ issue_keys.txt

ITREQ-12
ITREQ-14
ITREQ-19

 

□ attachement_check.py

import requests
import json
# Read configuration file
with open('config.json', 'r') as config_file:
    config = json.load(config_file)
jira_url = config['jira_url']
username = config['username']
api_token = config['api_token']
# Read issue key file
with open('issue_keys.txt', 'r') as issue_file:
    issue_keys = [line.strip() for line in issue_file.readlines()]
# Jira API request header
headers = {
    'Accept': 'application/json'
}
for issue_key in issue_keys:
    # Retrieve attachment list
    response = requests.get(
        f'{jira_url}/rest/api/2/issue/{issue_key}?fields=attachment',
        headers=headers,
        auth=(username, api_token)
    )
    # Response handling
    if response.status_code == 200:
        attachments = response.json().get('fields', {}).get('attachment', [])
        if attachments:
            print(f"Issue {issue_key} has attachments.")
        else:
            print(f"Issue {issue_key} has no attachments.")
    else:
        print(f"Failed to fetch attachments for issue {issue_key}: {response.status_code} - {response.text}")

 

5. Inform users by commenting on tickets containing attachments that the attachments are scheduled to be deleted soon.

 > Writing Python code using Copilot to avoid the hassle of manually commenting on each ticket individually.

 

<Description>

- write_comment.py : Code for adding comments to tickets.

 

write_comment.py

import requests
import json
# Read configuration file
with open('config.json', 'r') as config_file:
    config = json.load(config_file)
jira_url = config['jira_url']
username = config['username']
api_token = config['api_token']
# Read issue key file
with open('issue_keys2.txt', 'r') as issue_file:
    issue_keys = [line.strip() for line in issue_file.readlines()]
# Jira API request header
headers = {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
}
for issue_key in issue_keys:
    # Retrieve issue information
    issue_response = requests.get(
        f'{jira_url}/rest/api/2/issue/{issue_key}?fields=assignee,reporter',
        headers=headers,
        auth=(username, api_token)
    )
    if issue_response.status_code == 200:
        issue_data = issue_response.json()
        assignee = issue_data['fields']['assignee']['accountId']
        reporter = issue_data['fields']['reporter']['accountId']
        # Comment data
        comment_data = {
            "body": f"[~accountid:{assignee}], [~accountid:{reporter}] Hello. \r\n To protect personal information, we plan to delete all attachments in JIRA. \r\n\r\n Please note that all attachments in this JIRA will be deleted in 7 days.\r\n\r\n Thank you."
        }
        # Add comment request
        comment_response = requests.post(
            f'{jira_url}/rest/api/2/issue/{issue_key}/comment',
            headers=headers,
            auth=(username, api_token),
            data=json.dumps(comment_data)
        )
        if comment_response.status_code == 201:
            print(f"Comment added successfully to issue {issue_key}.")
        else:
            print(f"Failed to add comment to issue {issue_key}: {comment_response.status_code} - {comment_response.text}")
    else:
        print(f"Failed to fetch issue {issue_key}: {issue_response.status_code} - {issue_response.text}")

 

6. Delete all attachments included in the ticket.

 > Writing Python code using Copilot to avoid the hassle of manually deleting attachments for each ticket individually.  

 

<Description>

- delete.py : Code that retrieves the attachment ID for each issue key listed in issue_keys.txt and sends a delete method to delete the file

 

□ delete.py

import requests
import json
# Read configuration file
with open('config.json', 'r') as config_file:
    config = json.load(config_file)
jira_url = config['jira_url']
username = config['username']
api_token = config['api_token']
# Read issue key file
with open('issue_keys.txt', 'r') as issue_file:
    issue_keys = [line.strip() for line in issue_file.readlines()]
# Jira API request header
headers = {
    'Accept': 'application/json'
}
for issue_key in issue_keys:
    # Retrieve attachment list
    response = requests.get(
        f'{jira_url}/rest/api/2/issue/{issue_key}?fields=attachment',
        headers=headers,
        auth=(username, api_token)
    )
    # Response handling
    if response.status_code == 200:
        attachments = response.json().get('fields', {}).get('attachment', [])
        for attachment in attachments:
            attachment_id = attachment['id']
            # Delete attachment request.
            delete_response = requests.delete(
                f'{jira_url}/rest/api/2/attachment/{attachment_id}',
                headers=headers,
                auth=(username, api_token)
            )
            if delete_response.status_code == 204:
                print(f"Attachment {attachment_id} deleted successfully from issue {issue_key}.")
            else:
                print(f"Failed to delete attachment {attachment_id} from issue {issue_key}: {delete_response.status_code} - {delete_response.text}")
    else:
        print(f"Failed to fetch attachments for issue {issue_key}: {response.status_code} - {response.text}")