blob: f11f2757e2c0e5f0d56d329254b88004ce5afa6c [file] [log] [blame]
################################################################################
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
################################################################################
import abc
import logging
class FlinkJiraRule:
__metaclass__ = abc.ABCMeta
def __init__(self, jira_client, config, is_dry_run):
self.jira_client = jira_client
self.is_dry_run = is_dry_run
self.stale_days = config["stale_days"].get()
self.warning_days = config["warning_days"].get()
self.warning_label = config["warning_label"].get()
self.done_label = config["done_label"].get()
self.done_comment = config["done_comment"].get()
self.warning_comment = config["warning_comment"].get()
def get_issues(self, jql_query):
"""Queries the JIRA PI for all issues that match the given JQL Query
This method is necessary as requests tend to time out if the number of results reaches a certain number.
So, this method requests the results in multiple queries and returns a final list of all issues.
:param jql_query: the search query
:return: a list of issues matching the query
"""
limit = 200
current = 0
total = 1
issues = []
while current < total:
response = self.jira_client.jql(jql_query, limit=limit, start=current)
total = response["total"]
issues = issues + response["issues"]
current = len(issues)
logging.info(f'"{jql_query}" returned {len(issues)} issues')
return issues
def has_recently_updated_subtask(self, parent, updated_within_days):
find_subtasks_updated_within = (
f"parent = {parent} AND updated > startOfDay(-{updated_within_days}d)"
)
issues = self.get_issues(find_subtasks_updated_within)
return len(issues) > 0
def add_label(self, issue, label):
labels = issue["fields"]["labels"] + [label]
fields = {"labels": labels}
key = issue["key"]
if not self.is_dry_run:
self.jira_client.update_issue_field(key, fields)
else:
logging.info(f'DRY RUN ({key}): Adding label "{label}".')
def replace_label(self, issue, old_label, new_label):
labels = issue["fields"]["labels"] + [new_label]
labels.remove(old_label)
fields = {"labels": labels}
key = issue["key"]
if not self.is_dry_run:
self.jira_client.update_issue_field(key, fields)
else:
logging.info(
f'DRY RUN ({key}): Replace label "{old_label}" for "{new_label}".'
)
def add_comment(self, key, comment):
if not self.is_dry_run:
self.jira_client.issue_add_comment(key, comment)
else:
logging.info(f'DRY_RUN ({key}): Adding comment "{comment}".')
def close_issue(self, key):
if not self.is_dry_run:
self.jira_client.set_issue_status(
key, "Closed", fields={"resolution": {"name": "Auto Closed"}}
)
else:
logging.info(f"DRY_RUN (({key})): Closing.")
def unassign(self, key):
if not self.is_dry_run:
self.jira_client.assign_issue(key, None)
else:
logging.info(f"DRY_RUN (({key})): Unassigning.")
def set_priority(self, key, priority):
if not self.is_dry_run:
fields = {"priority": {"name": priority}}
self.jira_client.update_issue_field(key, fields)
else:
logging.info(f"DRY_RUN (({key})): Setting to {priority}")
@abc.abstractmethod
def run(self):
return
@abc.abstractmethod
def handle_stale_ticket(self, key):
return
def mark_stale_tickets_stale(self, jql_query):
logging.info(f"Looking for stale tickets.")
issues = self.get_issues(jql_query)
for issue in issues:
key = issue["key"]
issue = self.jira_client.get_issue(key)
if not self.has_recently_updated_subtask(key, self.stale_days):
logging.info(
f"Found https://issues.apache.org/jira/browse/{key}. It is marked stale now."
)
formatted_comment = self.warning_comment.format(
stale_days=self.stale_days,
warning_days=self.warning_days,
warning_label=self.warning_label,
)
self.add_label(issue, self.warning_label)
self.add_comment(key, formatted_comment)
else:
logging.info(
f"Found https://issues.apache.org/jira/browse/{key}, but is has recently updated Subtasks. "
f"Ignoring for now."
)
def handle_tickets_marked_stale(self, jql_query):
logging.info(f"Looking for ticket previously marked as {self.warning_label}.")
issues = self.get_issues(jql_query)
for issue in issues:
key = issue["key"]
logging.info(
f"Found https://issues.apache.org/jira/browse/{key}. It is now processed as stale."
)
formatted_comment = self.done_comment.format(
warning_days=self.warning_days,
warning_label=self.warning_label,
done_label=self.done_label,
)
self.add_comment(key, formatted_comment)
self.replace_label(issue, self.warning_label, self.done_label)
self.handle_stale_ticket(key)