From 4ef994eaa16918fab5e8332f0dfd1180560b0a38 Mon Sep 17 00:00:00 2001 From: ronykoz Date: Tue, 27 Oct 2020 16:53:18 +0200 Subject: [PATCH 01/19] Revert to 0.1.8 Add the option to restore cards from archive --- .../management/event_manager.py | 3 +- .../management/github_client.py | 90 ++-------------- .../management/project_manager.py | 69 +++--------- tests/test_files/project_manager_test.py | 100 +++++------------- tests/test_files/utils_test.py | 21 ++++ 5 files changed, 70 insertions(+), 213 deletions(-) create mode 100644 tests/test_files/utils_test.py diff --git a/src/github_automation/management/event_manager.py b/src/github_automation/management/event_manager.py index cc1ece1..f96f921 100644 --- a/src/github_automation/management/event_manager.py +++ b/src/github_automation/management/event_manager.py @@ -61,8 +61,7 @@ def load_project_column(self, column_name): def manage_issue_in_project(self, issue): if (self.config.remove and self.config.project_number in issue.get_associated_project() and not is_matching_issue(issue.labels, - self.config.must_have_labels, - self.config.cant_have_labels, + self.config.must_have_labels, self.config.cant_have_labels, self.config.filter_labels)): card_id = [_id for _id, value in issue.card_id_project.items() diff --git a/src/github_automation/management/github_client.py b/src/github_automation/management/github_client.py index d7447eb..9e7aa8b 100644 --- a/src/github_automation/management/github_client.py +++ b/src/github_automation/management/github_client.py @@ -510,89 +510,11 @@ def get_first_column_issues(self, owner, name, project_number, start_cards_curso } ''', {"owner": owner, "name": name, "projectNumber": project_number, "$start_cards_cursor": start_cards_cursor}) - def search_issues_by_query(self, query, start_cursor=None): - return self.execute_query(''' - query ($query: String!, $start_cursor: String){ - search(type: ISSUE, query: $query, first: 100, after: $start_cursor) { - ... on SearchResultItemConnection { - pageInfo { - hasNextPage - endCursor - } - issueCount - edges { - cursor - node { - ... on Issue { - title - number - id - labels(first:10) { - edges { - node { - name - } - } - } - milestone { - title - } - assignees(last: 10) { - edges { - node { - id - login - } - } - } - timelineItems(first: 10, itemTypes: [CROSS_REFERENCED_EVENT]) { - __typename - ... on IssueTimelineItemsConnection { - nodes { - ... on CrossReferencedEvent { - willCloseTarget - source { - __typename - ... on PullRequest { - state - isDraft - assignees(first: 10) { - nodes { - login - } - } - labels(first: 5) { - nodes { - name - } - } - reviewRequests(first: 1) { - totalCount - } - reviews(first: 1) { - totalCount - } - number - reviewDecision - } - } - } - } - } - } - } - } - } - } - } -} -''', {'query': query, 'start_cursor': start_cursor}) - def un_archive_card(self, card_id): return self.execute_query(''' mutation ($card_id: ID!, $isArchived: Boolean){ - updateProjectCard(input: {projectCardId: $card_id, isArchived: $isArchived}) { - projectCard { - isArchived - } - } - }''', {'card_id': card_id, "isArchived": False}) + updateProjectCard(input: {projectCardId: $card_id, isArchived: $isArchived}) { + projectCard { + isArchived + } + } + }''', {'card_id': card_id, "isArchived": False}) diff --git a/src/github_automation/management/project_manager.py b/src/github_automation/management/project_manager.py index 1550950..0f01e16 100644 --- a/src/github_automation/management/project_manager.py +++ b/src/github_automation/management/project_manager.py @@ -1,6 +1,5 @@ from __future__ import absolute_import -from github_automation.common.constants import OR from github_automation.common.utils import (get_column_issues_with_prev_column, get_first_column_issues, is_matching_issue) @@ -21,7 +20,7 @@ def __init__(self, configuration: Configuration, client=None, api_key=None): def construct_issue_object(self, github_issues): issues = {} - for edge in github_issues: + for edge in github_issues['edges']: node_data = edge['node'] issue_labels = get_labels(node_data['labels']['edges']) if is_matching_issue(issue_labels, self.config.must_have_labels, self.config.cant_have_labels, @@ -50,58 +49,22 @@ def get_github_project(self): return Project(**parse_project(project_builder['repository']['project'], self.config)) - @staticmethod - def construct_filters(cant_have_string, milestone_string, must_labels_string, or_labels, filter_labels): - filters = [] - base_filter = f'{cant_have_string} {milestone_string} {must_labels_string}' - for filter_label in filter_labels: - filter_string = f'{base_filter} label:{filter_label}' - if or_labels: - for or_label in or_labels: - filters.append(f'{filter_string} label:{or_label}') - else: - filters.append(filter_string) - - return filters - - @staticmethod - def get_filters(config): - cant_have_string = ' '.join([f'-label:{cant_have_label}' for cant_have_label in config.cant_have_labels]) - milestone_string = f'milestone:{config.filter_milestone}' if config.filter_milestone else '' - - must_labels = [] - or_labels = [] - for must_label in config.must_have_labels: - if OR in must_label: - or_labels.extend(must_label.split(OR)) - else: - must_labels.append(must_label) - - must_labels_string = ' '.join([f'label:{must_label}' for must_label in must_labels]) - - filters = ProjectManager.construct_filters(cant_have_string, milestone_string, must_labels_string, or_labels, - config.filter_labels) - return filters - def get_github_issues(self): - issues = [] - for issue_filter in self.get_filters(self.config): - response = self.client.search_issues_by_query( - query=f"repo:{self.config.project_owner}/{self.config.repository_name} is:open " - f"{issue_filter} -project:{self.config.project_owner}/" - f"{self.config.repository_name}/{self.config.project_number}" - ) - issues.extend(response.get('search', {}).get('edges', [])) - - while response.get('search', {}).get('pageInfo').get('hasNextPage'): - after = response.get('search', {}).get('pageInfo').get('endCursor') - response = self.client.search_issues_by_query( - query=f"repo:{self.config.project_owner}/{self.config.repository_name} is:open " - f"{issue_filter} -project:{self.config.project_owner}/" - f"{self.config.repository_name}/{self.config.project_number}", - start_cursor=after - ) - issues.extend(response.get('search', {}).get('edges', [])) + response = self.client.get_github_issues(owner=self.config.project_owner, + name=self.config.repository_name, + labels=self.config.filter_labels, + milestone=self.config.filter_milestone, + after=None) + issues = response.get('repository', {}).get('issues', {}) + + while response.get('repository', {}).get('issues', {}).get('pageInfo').get('hasNextPage'): + after = response.get('repository', {}).get('issues', {}).get('pageInfo').get('endCursor') + response = self.client.get_github_issues(owner=self.config.project_owner, + name=self.config.repository_name, + after=after, + labels=self.config.filter_labels, + milestone=self.config.filter_milestone) + issues.get('edges').extend(response.get('repository', {}).get('issues', {}).get('edges')) return self.construct_issue_object(issues) diff --git a/tests/test_files/project_manager_test.py b/tests/test_files/project_manager_test.py index c659b5f..d79b824 100644 --- a/tests/test_files/project_manager_test.py +++ b/tests/test_files/project_manager_test.py @@ -1,6 +1,5 @@ import os -from github_automation.common.utils import is_matching_issue from github_automation.management.configuration import Configuration from github_automation.management.project_manager import ProjectManager @@ -315,29 +314,33 @@ def test_loading(): } issues = { - "search": { - "pageInfo": { - "hasNextPage": True, - "endCursor": "cursor" - }, - "edges": [ - { - "node": issue - } - ] + "repository": { + "issues": { + "pageInfo": { + "hasNextPage": True, + "endCursor": "cursor" + }, + "edges": [ + { + "node": issue + } + ] + } } } issues_with_no_after = { - "search": { - "pageInfo": { - "hasNextPage": False, - "endCursor": "cursor" - }, - "edges": [ - { - "node": issue - } - ] + "repository": { + "issues": { + "pageInfo": { + "hasNextPage": False, + "endCursor": "cursor" + }, + "edges": [ + { + "node": issue + } + ] + } } } @@ -369,8 +372,8 @@ def get_first_column_issues(self, **kwargs): def get_column_issues(self, **kwargs): return column2 - def search_issues_by_query(self, **kwargs): - if 'start_cursor' not in kwargs: + def get_github_issues(self, **kwargs): + if not kwargs['after']: return issues return issues_with_no_after @@ -401,54 +404,3 @@ def move_to_specific_place_in_column(self, **kwargs): manager.manage() assert manager.project.is_in_column("In progress", issue_id) is True assert manager.project.columns["In progress"].cards[0].issue.id == issue_id - - -def test_matching_issue_filter(): - config = Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini')) - config.load_properties() - - assert is_matching_issue(['test', 'bug'], config.must_have_labels, config.cant_have_labels, - config.filter_labels) is True - assert is_matching_issue(['not test', 'bug'], config.must_have_labels, config.cant_have_labels, - config.filter_labels) is False - assert is_matching_issue(['not test', 'test', 'bug'], - config.must_have_labels, config.cant_have_labels, - config.filter_labels) is False - - config.filter_labels = ['not bug'] - assert is_matching_issue(['bug', 'test'], config.must_have_labels, config.cant_have_labels, - config.filter_labels) is False - assert is_matching_issue(['not bug', 'test'], config.must_have_labels, config.cant_have_labels, - config.filter_labels) is True - - config.must_have_labels = ['test||something'] - assert is_matching_issue(['not bug', 'test'], config.must_have_labels, config.cant_have_labels, - config.filter_labels) is True - assert is_matching_issue(['not bug', 'something'], config.must_have_labels, config.cant_have_labels, - config.filter_labels) is True - assert is_matching_issue(['not bug', 'else'], config.must_have_labels, config.cant_have_labels, - config.filter_labels) is False - - -def test_get_filters(): - config = Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini')) - config.load_properties() - - filters = ProjectManager.get_filters(config) - - assert len(filters) == 1 - assert ' label:bug' in filters[0] - assert ' label:test' in filters[0] - assert '-label:not test' in filters[0] - - config.filter_labels = ['one', 'two'] - config.must_have_labels = ['three', 'four||five'] - config.cant_have_labels = ['six', 'seven'] - filters = ProjectManager.get_filters(config) - - assert len(filters) == 4 - for filter_str in filters: - assert ('label:one' in filter_str and 'label:two' not in filter_str) or ('label:one' not in filter_str - and 'label:two' in filter_str) - assert ('label:four' in filter_str and 'label:five' not in filter_str) or ('label:four' not in filter_str - and 'label:five' in filter_str) diff --git a/tests/test_files/utils_test.py b/tests/test_files/utils_test.py new file mode 100644 index 0000000..a620ae1 --- /dev/null +++ b/tests/test_files/utils_test.py @@ -0,0 +1,21 @@ +from __future__ import absolute_import + +import pytest +from github_automation.common.constants import OR +from github_automation.common.utils import is_matching_issue + + +@pytest.mark.parametrize('issue_labels, must_have_labels, cant_have_labels, filter_labels, result', + [ + [['test', '1'], ['test'], ['2'], ['1'], True], + [['test', '1', '3'], ['test'], ['2'], ['1'], True], + [['test', '1', '3'], ['1'], ['2'], ['test'], True], + [['test', '1', '3'], ['1'], ['3'], ['test'], False], + [['test', '1', '3'], ['1'], ['3', '4'], ['test'], False], + [['test', '1', '3'], ['12'], ['3', '4'], ['test1'], False], + [['test', '1', '3'], ['1'], ['3', '4'], ['test1'], False], + [['test', '1', '3'], ['12'], ['3', '4'], ['test'], False], + [['test', '1', '3'], [f'some{OR}text'], ['3'], ['test'], False] + ]) +def test_is_matching_issue(issue_labels, must_have_labels, cant_have_labels, filter_labels, result): + assert is_matching_issue(issue_labels, must_have_labels, cant_have_labels, filter_labels) is result From f19798f885742b54690793f97535cf735b69f9c7 Mon Sep 17 00:00:00 2001 From: ronykoz Date: Tue, 27 Oct 2020 17:05:19 +0200 Subject: [PATCH 02/19] dismiss closed issues --- src/github_automation/core/issue/issue.py | 7 +++++-- src/github_automation/management/event_manager.py | 3 +++ src/github_automation/management/github_client.py | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/github_automation/core/issue/issue.py b/src/github_automation/core/issue/issue.py index 484731a..095b429 100644 --- a/src/github_automation/core/issue/issue.py +++ b/src/github_automation/core/issue/issue.py @@ -66,16 +66,19 @@ def parse_issue(github_issue): "pull_request": _get_pull_request(github_issue), "labels": get_labels(github_issue.get('labels', {}).get('edges', [])), "milestone": _get_milestone(github_issue), - "card_id_to_project": _extract_project_cards(github_issue.get('projectCards', {})) + "card_id_to_project": _extract_project_cards(github_issue.get('projectCards', {})), + "state": github_issue.get('state', '') } class Issue(object): def __init__(self, id: str, title: str, number: int, assignees: list = None, pull_request: PullRequest = None, - labels: list = None, milestone: str = '', card_id_to_project: dict = None, priority_list: list = None): + labels: list = None, milestone: str = '', card_id_to_project: dict = None, priority_list: list = None, + state: str = ''): self.id = id self.title = title self.number = number + self.state = state self.assignees = assignees if assignees else [] self.pull_request = pull_request diff --git a/src/github_automation/management/event_manager.py b/src/github_automation/management/event_manager.py index f96f921..a923ae2 100644 --- a/src/github_automation/management/event_manager.py +++ b/src/github_automation/management/event_manager.py @@ -99,6 +99,9 @@ def get_issue_object(self): def run(self): issue = self.get_issue_object() + if issue.state == 'closed': + print("The issue is closed, not taking an action.") + return for conf_path in self.conf_paths: self.config = Configuration(conf_path, self.verbose, self.quiet, self.log_path) diff --git a/src/github_automation/management/github_client.py b/src/github_automation/management/github_client.py index 9e7aa8b..145396f 100644 --- a/src/github_automation/management/github_client.py +++ b/src/github_automation/management/github_client.py @@ -386,6 +386,7 @@ def get_issue(self, owner, name, issue_number): title id number + state milestone { title } From e287d96a20d23e5e4008fa21c4afd18c120cc019 Mon Sep 17 00:00:00 2001 From: ronykoz Date: Tue, 17 Nov 2020 17:11:22 +0200 Subject: [PATCH 03/19] update automation --- src/github_automation/core/project/project.py | 22 +++++++++---------- .../management/github_client.py | 14 ++++++++++++ .../management/project_manager.py | 2 +- tests/test_files/project_manager_test.py | 12 +++++----- tests/test_files/project_test.py | 4 ++-- 5 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/github_automation/core/project/project.py b/src/github_automation/core/project/project.py index b3718d3..7f5cf4b 100644 --- a/src/github_automation/core/project/project.py +++ b/src/github_automation/core/project/project.py @@ -281,19 +281,17 @@ def get_current_location(self, issue_id): return None, None - def move_issues(self, client, config: Configuration): + def move_issues(self, client, config: Configuration, all_issues): # todo: add explanation that we are relying on the github automation to move closed issues to the Done queue - for column in self.columns.values(): - issues = column.get_issues() - for issue in issues: - column_name_before, card_id = self.get_current_location(issue.id) - column_name_after = self.get_matching_column(issue, config) - column_id = self.columns[column_name_after].id if column_name_after else '' - if not column_id or column_name_before == column_name_after: - continue - - self.move_issue(client, issue, column_name_after, config) - self.columns[column_name_before].remove_card(card_id) + for issue in all_issues.values(): + column_name_before, card_id = self.get_current_location(issue.id) + column_name_after = self.get_matching_column(issue, config) + column_id = self.columns[column_name_after].id if column_name_after else '' + if not column_id or column_name_before == column_name_after: + continue + + self.move_issue(client, issue, column_name_after, config) + self.columns[column_name_before].remove_card(card_id) def move_issue(self, client, issue, column_name, config: Configuration): card_id = [_id for _id, value in issue.card_id_project.items() diff --git a/src/github_automation/management/github_client.py b/src/github_automation/management/github_client.py index 145396f..0ea8521 100644 --- a/src/github_automation/management/github_client.py +++ b/src/github_automation/management/github_client.py @@ -446,6 +446,13 @@ def get_column_issues(self, owner, name, project_number, prev_column_id, start_c } } } + assignees(first: 10) { + edges { + node { + login + } + } + } } } } @@ -494,6 +501,13 @@ def get_first_column_issues(self, owner, name, project_number, start_cards_curso } } } + assignees(first: 10) { + edges { + node { + login + } + } + } } ... on PullRequest { id diff --git a/src/github_automation/management/project_manager.py b/src/github_automation/management/project_manager.py index 0f01e16..0447b5c 100644 --- a/src/github_automation/management/project_manager.py +++ b/src/github_automation/management/project_manager.py @@ -83,4 +83,4 @@ def manage(self): self.project.sort_issues_in_columns(self.client, self.config) if self.config.move: - self.project.move_issues(self.client, self.config) + self.project.move_issues(self.client, self.config, self.matching_issues) diff --git a/tests/test_files/project_manager_test.py b/tests/test_files/project_manager_test.py index d79b824..9d9c32c 100644 --- a/tests/test_files/project_manager_test.py +++ b/tests/test_files/project_manager_test.py @@ -195,7 +195,7 @@ def test_loading(): "columns": { "nodes": [ { - "name": "testing" + "name": "Queue" } ] } @@ -208,7 +208,7 @@ def test_loading(): "columns": { "nodes": [ { - "name": "Queue" + "name": "In progress" } ] } @@ -399,8 +399,10 @@ def move_to_specific_place_in_column(self, **kwargs): assert issues == ['issue 3', 'issue 2'] manager.project.columns['In progress'].remove_card("56565=") - assert manager.project.is_in_column("In progress", "56567=") is False + issues = [card.issue.title for card in manager.project.columns['In progress'].cards] + assert len(issues) == 1 + assert manager.project.is_in_column("In progress", "1234=") is True + assert manager.project.is_in_column("In progress", "56565=") is False manager.manage() - assert manager.project.is_in_column("In progress", issue_id) is True - assert manager.project.columns["In progress"].cards[0].issue.id == issue_id + assert manager.project.is_in_column("In progress", "56565=") is False diff --git a/tests/test_files/project_test.py b/tests/test_files/project_test.py index 9e57fe4..78ed131 100644 --- a/tests/test_files/project_test.py +++ b/tests/test_files/project_test.py @@ -516,11 +516,11 @@ def add_to_column(*args, **kwargs): def move_to_specific_place_in_column(*args, **kwargs): return - project.move_issues(MockClient(), config) + project.move_issues(MockClient(), config, {'1': issue}) assert project.is_in_column("Queue", "1") is False assert project.is_in_column("In progress", "1") is True # Move within the same column - project.move_issues(MockClient(), config) + project.move_issues(MockClient(), config, {'1': issue}) assert project.is_in_column("Queue", "1") is False assert project.is_in_column("In progress", "1") is True From 3326ab9912750810bb1e85c11d4842fb25fee7b4 Mon Sep 17 00:00:00 2001 From: ronykoz Date: Tue, 17 Nov 2020 17:18:43 +0200 Subject: [PATCH 04/19] Skipping the move on closed issues --- src/github_automation/core/project/project.py | 4 ++++ src/github_automation/management/github_client.py | 1 + 2 files changed, 5 insertions(+) diff --git a/src/github_automation/core/project/project.py b/src/github_automation/core/project/project.py index 7f5cf4b..153d3c0 100644 --- a/src/github_automation/core/project/project.py +++ b/src/github_automation/core/project/project.py @@ -294,6 +294,10 @@ def move_issues(self, client, config: Configuration, all_issues): self.columns[column_name_before].remove_card(card_id) def move_issue(self, client, issue, column_name, config: Configuration): + if issue.state == 'closed': + config.logger.debug(f'skipping {issue.title} because the issue is closed') + return + card_id = [_id for _id, value in issue.card_id_project.items() if value['project_number'] == config.project_number][0] diff --git a/src/github_automation/management/github_client.py b/src/github_automation/management/github_client.py index 0ea8521..304db69 100644 --- a/src/github_automation/management/github_client.py +++ b/src/github_automation/management/github_client.py @@ -152,6 +152,7 @@ def get_github_issues(self, owner, name, after, labels, milestone): title id number + state milestone { title } From 966a7faa31a7770d9dbf7937fa399c6add8c4ec1 Mon Sep 17 00:00:00 2001 From: ronykoz Date: Tue, 17 Nov 2020 17:41:21 +0200 Subject: [PATCH 05/19] adding log messages --- src/github_automation/core/project/project.py | 4 +++- tests/test_files/project_test.py | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/github_automation/core/project/project.py b/src/github_automation/core/project/project.py index 153d3c0..035680b 100644 --- a/src/github_automation/core/project/project.py +++ b/src/github_automation/core/project/project.py @@ -73,6 +73,7 @@ def add_card(self, card_id, new_issue, client): exception_msg = str(ex) if 'The card must not be archived' in exception_msg: try: + self.config.logger.info(f"Un-archiving the card of {new_issue.title}") client.un_archive_card(card_id) client.add_to_column(card_id=card_id, column_id=self.id) @@ -99,6 +100,7 @@ def add_card(self, card_id, new_issue, client): exception_msg = str(ex) if 'The card must not be archived' in exception_msg: try: + self.config.logger.info(f"Un-archiving the card of {new_issue.title}") client.un_archive_card(card_id) client.move_to_specific_place_in_column(card_id=card_id, column_id=self.id, @@ -287,7 +289,7 @@ def move_issues(self, client, config: Configuration, all_issues): column_name_before, card_id = self.get_current_location(issue.id) column_name_after = self.get_matching_column(issue, config) column_id = self.columns[column_name_after].id if column_name_after else '' - if not column_id or column_name_before == column_name_after: + if not column_id or column_name_before == column_name_after or issue.state == 'closed': continue self.move_issue(client, issue, column_name_after, config) diff --git a/tests/test_files/project_test.py b/tests/test_files/project_test.py index 78ed131..e2cd224 100644 --- a/tests/test_files/project_test.py +++ b/tests/test_files/project_test.py @@ -524,3 +524,7 @@ def move_to_specific_place_in_column(*args, **kwargs): project.move_issues(MockClient(), config, {'1': issue}) assert project.is_in_column("Queue", "1") is False assert project.is_in_column("In progress", "1") is True + + issue.state = 'closed' + project.move_issue(MockClient(), issue, 'In progress', config) + assert project.is_in_column("In progress", "1") is True From 509f840a4516c4ad2a9de586a233ca9725e2fda7 Mon Sep 17 00:00:00 2001 From: ronykoz Date: Thu, 19 Nov 2020 14:39:57 +0200 Subject: [PATCH 06/19] fixing the usage of a card marker --- src/github_automation/management/github_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/github_automation/management/github_client.py b/src/github_automation/management/github_client.py index 304db69..5b798b4 100644 --- a/src/github_automation/management/github_client.py +++ b/src/github_automation/management/github_client.py @@ -465,7 +465,7 @@ def get_column_issues(self, owner, name, project_number, prev_column_id, start_c } } ''', {"owner": owner, "name": name, "projectNumber": project_number, "prevColumnID": prev_column_id, - "$start_cards_cursor": start_cards_cursor}) + "start_cards_cursor": start_cards_cursor}) def get_first_column_issues(self, owner, name, project_number, start_cards_cursor=''): return self.execute_query(''' @@ -524,7 +524,7 @@ def get_first_column_issues(self, owner, name, project_number, start_cards_curso } } } - ''', {"owner": owner, "name": name, "projectNumber": project_number, "$start_cards_cursor": start_cards_cursor}) + ''', {"owner": owner, "name": name, "projectNumber": project_number, "start_cards_cursor": start_cards_cursor}) def un_archive_card(self, card_id): return self.execute_query(''' mutation ($card_id: ID!, $isArchived: Boolean){ From d6265ca091912150102d0f4bd24fec180a98c54e Mon Sep 17 00:00:00 2001 From: ronykoz Date: Thu, 19 Nov 2020 15:13:35 +0200 Subject: [PATCH 07/19] fixing the usage of a card marker --- src/github_automation/management/event_manager.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/github_automation/management/event_manager.py b/src/github_automation/management/event_manager.py index a923ae2..42a9b76 100644 --- a/src/github_automation/management/event_manager.py +++ b/src/github_automation/management/event_manager.py @@ -70,6 +70,7 @@ def manage_issue_in_project(self, issue): return matching_column_name = Project.get_matching_column(issue, self.config) + self.config.logger(f"the right column is {matching_column_name}") if self.config.add and self.config.project_number not in issue.get_associated_project(): project = self.load_project_column(matching_column_name) @@ -78,6 +79,7 @@ def manage_issue_in_project(self, issue): column_name_before = [value['project_column'] for _id, value in issue.card_id_project.items() if value['project_number'] == self.config.project_number][0] + self.config.logger(f"the column was {column_name_before}") if (self.config.add and not column_name_before) or \ (self.config.move and matching_column_name != column_name_before): project = self.load_project_column(matching_column_name) From cb433bfea07b9b76085a4f1ece4708ba6b156624 Mon Sep 17 00:00:00 2001 From: ronykoz Date: Thu, 19 Nov 2020 15:16:26 +0200 Subject: [PATCH 08/19] fixing the usage of a card marker --- src/github_automation/management/event_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/github_automation/management/event_manager.py b/src/github_automation/management/event_manager.py index 42a9b76..25721e6 100644 --- a/src/github_automation/management/event_manager.py +++ b/src/github_automation/management/event_manager.py @@ -70,7 +70,7 @@ def manage_issue_in_project(self, issue): return matching_column_name = Project.get_matching_column(issue, self.config) - self.config.logger(f"the right column is {matching_column_name}") + print(f"the right column is {matching_column_name}") if self.config.add and self.config.project_number not in issue.get_associated_project(): project = self.load_project_column(matching_column_name) @@ -79,7 +79,7 @@ def manage_issue_in_project(self, issue): column_name_before = [value['project_column'] for _id, value in issue.card_id_project.items() if value['project_number'] == self.config.project_number][0] - self.config.logger(f"the column was {column_name_before}") + print(f"the column was {column_name_before}") if (self.config.add and not column_name_before) or \ (self.config.move and matching_column_name != column_name_before): project = self.load_project_column(matching_column_name) From abfb4e4e811a7b5dceb8c3a3663d7766791a1815 Mon Sep 17 00:00:00 2001 From: ronykoz Date: Thu, 19 Nov 2020 15:26:49 +0200 Subject: [PATCH 09/19] fixing the usage of a card marker --- src/github_automation/management/event_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/github_automation/management/event_manager.py b/src/github_automation/management/event_manager.py index 25721e6..7947f3b 100644 --- a/src/github_automation/management/event_manager.py +++ b/src/github_automation/management/event_manager.py @@ -97,6 +97,7 @@ def get_issue_object(self): issue_response = self.client.get_issue( self.project_owner, self.repository_name, issue_number) # need to address the remove here issue = Issue(**parse_issue(issue_response['repository']['issue'])) + print(issue_response['repository']['issue']) return issue def run(self): From 46e1375bc8031476662e52d2ddc86df43c12501b Mon Sep 17 00:00:00 2001 From: ronykoz Date: Thu, 19 Nov 2020 15:34:37 +0200 Subject: [PATCH 10/19] fixing the usage of a card marker --- src/github_automation/management/event_manager.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/github_automation/management/event_manager.py b/src/github_automation/management/event_manager.py index 7947f3b..a923ae2 100644 --- a/src/github_automation/management/event_manager.py +++ b/src/github_automation/management/event_manager.py @@ -70,7 +70,6 @@ def manage_issue_in_project(self, issue): return matching_column_name = Project.get_matching_column(issue, self.config) - print(f"the right column is {matching_column_name}") if self.config.add and self.config.project_number not in issue.get_associated_project(): project = self.load_project_column(matching_column_name) @@ -79,7 +78,6 @@ def manage_issue_in_project(self, issue): column_name_before = [value['project_column'] for _id, value in issue.card_id_project.items() if value['project_number'] == self.config.project_number][0] - print(f"the column was {column_name_before}") if (self.config.add and not column_name_before) or \ (self.config.move and matching_column_name != column_name_before): project = self.load_project_column(matching_column_name) @@ -97,7 +95,6 @@ def get_issue_object(self): issue_response = self.client.get_issue( self.project_owner, self.repository_name, issue_number) # need to address the remove here issue = Issue(**parse_issue(issue_response['repository']['issue'])) - print(issue_response['repository']['issue']) return issue def run(self): From 9098804a66249503160faf3d7cfab73603e27f76 Mon Sep 17 00:00:00 2001 From: ronykoz Date: Thu, 19 Nov 2020 15:58:03 +0200 Subject: [PATCH 11/19] adding debug msg --- src/github_automation/management/event_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/github_automation/management/event_manager.py b/src/github_automation/management/event_manager.py index a923ae2..fd6f1c5 100644 --- a/src/github_automation/management/event_manager.py +++ b/src/github_automation/management/event_manager.py @@ -80,6 +80,7 @@ def manage_issue_in_project(self, issue): if value['project_number'] == self.config.project_number][0] if (self.config.add and not column_name_before) or \ (self.config.move and matching_column_name != column_name_before): + print(f'Moving {issue.title} from {column_name_before}') project = self.load_project_column(matching_column_name) project.move_issue(self.client, issue, matching_column_name, self.config) return From ebfe6b4b69d24f72b4e0e1cf91763dea2303a09b Mon Sep 17 00:00:00 2001 From: ronykoz Date: Thu, 19 Nov 2020 16:07:27 +0200 Subject: [PATCH 12/19] debug msg --- src/github_automation/management/event_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/github_automation/management/event_manager.py b/src/github_automation/management/event_manager.py index fd6f1c5..cca5171 100644 --- a/src/github_automation/management/event_manager.py +++ b/src/github_automation/management/event_manager.py @@ -96,6 +96,7 @@ def get_issue_object(self): issue_response = self.client.get_issue( self.project_owner, self.repository_name, issue_number) # need to address the remove here issue = Issue(**parse_issue(issue_response['repository']['issue'])) + print(issue.card_id_project) return issue def run(self): From 7f7374373428a6d6d98440060dc89392884ff8cb Mon Sep 17 00:00:00 2001 From: ronykoz Date: Sun, 22 Nov 2020 10:41:03 +0200 Subject: [PATCH 13/19] fix the detection of project column card --- src/github_automation/core/issue/issue.py | 2 +- .../management/github_client.py | 24 +++++++------------ 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/github_automation/core/issue/issue.py b/src/github_automation/core/issue/issue.py index 095b429..c54d3da 100644 --- a/src/github_automation/core/issue/issue.py +++ b/src/github_automation/core/issue/issue.py @@ -52,7 +52,7 @@ def _extract_project_cards(project_cards): "project_number": node['project']['number'] } if node['project']['columns']['nodes']: - card_id_project[node['id']]['project_column'] = node['project']['columns']['nodes'][0]['name'] + card_id_project[node['id']]['project_column'] = node['column']['name'] return card_id_project diff --git a/src/github_automation/management/github_client.py b/src/github_automation/management/github_client.py index 5b798b4..1feb1dc 100644 --- a/src/github_automation/management/github_client.py +++ b/src/github_automation/management/github_client.py @@ -104,15 +104,13 @@ def get_github_issues(self, owner, name, after, labels, milestone): projectCards(first:5){ nodes{ id + column { + name + } project{ number - columns(first:1){ - nodes { - name - } } } - } } timelineItems(first:10, itemTypes:[CROSS_REFERENCED_EVENT]){ __typename @@ -191,13 +189,11 @@ def get_github_issues(self, owner, name, after, labels, milestone): projectCards(first:5){ nodes{ id + column { + name + } project{ number - columns(first:1){ - nodes { - name - } - } } } } @@ -339,13 +335,11 @@ def get_issue(self, owner, name, issue_number): projectCards(first:5){ nodes{ id + column { + name + } project{ number - columns(first:1){ - nodes { - name - } - } } } } From fed5be224ba092fe45baf8d0329fbcc93aec4a00 Mon Sep 17 00:00:00 2001 From: ronykoz Date: Sun, 22 Nov 2020 11:15:55 +0200 Subject: [PATCH 14/19] update tests per column change --- src/github_automation/core/issue/issue.py | 2 +- tests/test_files/event_manager_test.py | 24 ++++-------- tests/test_files/issue_test.py | 48 ++++++++--------------- tests/test_files/project_manager_test.py | 24 ++++-------- 4 files changed, 33 insertions(+), 65 deletions(-) diff --git a/src/github_automation/core/issue/issue.py b/src/github_automation/core/issue/issue.py index c54d3da..1e098b3 100644 --- a/src/github_automation/core/issue/issue.py +++ b/src/github_automation/core/issue/issue.py @@ -51,7 +51,7 @@ def _extract_project_cards(project_cards): card_id_project[node['id']] = { "project_number": node['project']['number'] } - if node['project']['columns']['nodes']: + if 'column' in node: card_id_project[node['id']]['project_column'] = node['column']['name'] return card_id_project diff --git a/tests/test_files/event_manager_test.py b/tests/test_files/event_manager_test.py index 53ce346..b398e25 100644 --- a/tests/test_files/event_manager_test.py +++ b/tests/test_files/event_manager_test.py @@ -21,28 +21,20 @@ def test_loading_event_manager(): "nodes": [ { "id": "id=", + "column": { + "name": "testing" + }, "project": { - "number": 1, - "columns": { - "nodes": [ - { - "name": "testing" - } - ] - } + "number": 1 } }, { "id": "id2=", + "column": { + "name": "Queue" + }, "project": { - "number": 2, - "columns": { - "nodes": [ - { - "name": "Queue" - } - ] - } + "number": 2 } } ] diff --git a/tests/test_files/issue_test.py b/tests/test_files/issue_test.py index 8e313c4..a5a733b 100644 --- a/tests/test_files/issue_test.py +++ b/tests/test_files/issue_test.py @@ -15,28 +15,20 @@ def test_parse_issue(): "nodes": [ { "id": "id=", + "column": { + "name": "testing" + }, "project": { - "number": 1, - "columns": { - "nodes": [ - { - "name": "testing" - } - ] - } + "number": 1 } }, { "id": "id2=", + "column": { + "name": "Queue" + }, "project": { - "number": 2, - "columns": { - "nodes": [ - { - "name": "Queue" - } - ] - } + "number": 2 } } ] @@ -132,28 +124,20 @@ def test_issue_params(): "nodes": [ { "id": "id=", + "column": { + "name": "testing" + }, "project": { - "number": 1, - "columns": { - "nodes": [ - { - "name": "testing" - } - ] - } + "number": 1 } }, { "id": "id2=", + "column": { + "name": "Queue" + }, "project": { - "number": 2, - "columns": { - "nodes": [ - { - "name": "Queue" - } - ] - } + "number": 2 } } ] diff --git a/tests/test_files/project_manager_test.py b/tests/test_files/project_manager_test.py index 9d9c32c..ebd552c 100644 --- a/tests/test_files/project_manager_test.py +++ b/tests/test_files/project_manager_test.py @@ -190,28 +190,20 @@ def test_loading(): "nodes": [ { "id": "id=", + "column": { + "name": "Queue" + }, "project": { - "number": 1, - "columns": { - "nodes": [ - { - "name": "Queue" - } - ] - } + "number": 1 } }, { "id": "id2=", + "column": { + "name": "In progress" + }, "project": { - "number": 2, - "columns": { - "nodes": [ - { - "name": "In progress" - } - ] - } + "number": 2 } } ] From bdddedcad3ca6b877188f9628a24d0e546a061a5 Mon Sep 17 00:00:00 2001 From: ronykoz Date: Sun, 22 Nov 2020 11:38:36 +0200 Subject: [PATCH 15/19] fix a case in which an issue is in the project but not in a column --- src/github_automation/core/issue/issue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/github_automation/core/issue/issue.py b/src/github_automation/core/issue/issue.py index 1e098b3..937e77f 100644 --- a/src/github_automation/core/issue/issue.py +++ b/src/github_automation/core/issue/issue.py @@ -51,7 +51,7 @@ def _extract_project_cards(project_cards): card_id_project[node['id']] = { "project_number": node['project']['number'] } - if 'column' in node: + if 'column' in node and node['column'] and 'key' in node['column']: card_id_project[node['id']]['project_column'] = node['column']['name'] return card_id_project From 197f5164fa5ac879eb7ea3efdf1d4a384d04b8fb Mon Sep 17 00:00:00 2001 From: ronykoz Date: Sun, 22 Nov 2020 11:57:13 +0200 Subject: [PATCH 16/19] removing debug print --- src/github_automation/management/event_manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/github_automation/management/event_manager.py b/src/github_automation/management/event_manager.py index cca5171..fd6f1c5 100644 --- a/src/github_automation/management/event_manager.py +++ b/src/github_automation/management/event_manager.py @@ -96,7 +96,6 @@ def get_issue_object(self): issue_response = self.client.get_issue( self.project_owner, self.repository_name, issue_number) # need to address the remove here issue = Issue(**parse_issue(issue_response['repository']['issue'])) - print(issue.card_id_project) return issue def run(self): From b71645326ebbd189256a20c5fa7c93f1624105d9 Mon Sep 17 00:00:00 2001 From: ronykoz Date: Sun, 22 Nov 2020 12:05:23 +0200 Subject: [PATCH 17/19] update test --- src/github_automation/core/issue/issue.py | 2 +- tests/test_files/issue_test.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/github_automation/core/issue/issue.py b/src/github_automation/core/issue/issue.py index 937e77f..c2c8e6e 100644 --- a/src/github_automation/core/issue/issue.py +++ b/src/github_automation/core/issue/issue.py @@ -51,7 +51,7 @@ def _extract_project_cards(project_cards): card_id_project[node['id']] = { "project_number": node['project']['number'] } - if 'column' in node and node['column'] and 'key' in node['column']: + if 'column' in node and node['column'] and 'name' in node['column']: card_id_project[node['id']]['project_column'] = node['column']['name'] return card_id_project diff --git a/tests/test_files/issue_test.py b/tests/test_files/issue_test.py index a5a733b..6530f99 100644 --- a/tests/test_files/issue_test.py +++ b/tests/test_files/issue_test.py @@ -219,6 +219,8 @@ def test_issue_params(): assert issue.id == issue_id assert issue.title == title assert issue.number == 1 + assert 'project_column' in issue.card_id_project['id='] + assert issue.card_id_project['id=']['project_column'] == 'testing' assert sorted(issue.labels) == sorted(labels) assert issue.priority_rank == 1 assert issue.milestone == "test" From db4686da6b3585a857ec175903e6882ea2608b5a Mon Sep 17 00:00:00 2001 From: ronykoz Date: Sun, 22 Nov 2020 13:25:11 +0200 Subject: [PATCH 18/19] adding ignore msg for PR events --- .../management/event_manager.py | 12 +++++++++++- tests/test_files/event_manager_test.py | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/github_automation/management/event_manager.py b/src/github_automation/management/event_manager.py index fd6f1c5..62849da 100644 --- a/src/github_automation/management/event_manager.py +++ b/src/github_automation/management/event_manager.py @@ -32,7 +32,11 @@ def __init__(self, conf: str, event: str, quiet: bool = False, log_path: str = ' @staticmethod def get_issue_number(event): - return event['issue']['number'] + if 'issue' in event: + return event['issue']['number'] + else: + print("This is not an issue, and we still do not support other github entities in the project.") + return def get_prev_column_cursor(self, column_name): layout = self.client.get_project_layout(owner=self.config.project_owner, @@ -93,6 +97,9 @@ def manage_issue_in_project(self, issue): def get_issue_object(self): issue_number = self.get_issue_number(self.event) + if issue_number is None: + return # In case the event is not for an issue + issue_response = self.client.get_issue( self.project_owner, self.repository_name, issue_number) # need to address the remove here issue = Issue(**parse_issue(issue_response['repository']['issue'])) @@ -100,6 +107,9 @@ def get_issue_object(self): def run(self): issue = self.get_issue_object() + if issue is None: + return # In case the event is not for an issue + if issue.state == 'closed': print("The issue is closed, not taking an action.") return diff --git a/tests/test_files/event_manager_test.py b/tests/test_files/event_manager_test.py index b398e25..23ddd64 100644 --- a/tests/test_files/event_manager_test.py +++ b/tests/test_files/event_manager_test.py @@ -611,3 +611,22 @@ def move_to_specific_place_in_column(self, **kwargs): event=json.dumps({"text": "text"})) manager.run() assert len(project_object.get_all_issue_ids()) == 1 + + +def test_loading_event_manager_without_an_issue(): + event = { + "action": "some action", + "pull_request": { + "number": 1 + } + } + + class mock_client(object): + def get_issue(*args, **kwargs): + return + + client = mock_client() + manager = EventManager(os.path.join(MOCK_FOLDER_PATH, 'conf.ini'), client=client, event=json.dumps(event)) + + issue_object = manager.get_issue_object() + assert issue_object is None From 86725a179ad25b160ee2fc715ee39f92f18c643f Mon Sep 17 00:00:00 2001 From: ronykoz Date: Sun, 22 Nov 2020 13:36:29 +0200 Subject: [PATCH 19/19] add a test for a closed issue event --- tests/test_files/event_manager_test.py | 185 +++++++++++++++++++++++++ 1 file changed, 185 insertions(+) diff --git a/tests/test_files/event_manager_test.py b/tests/test_files/event_manager_test.py index 23ddd64..8c37de0 100644 --- a/tests/test_files/event_manager_test.py +++ b/tests/test_files/event_manager_test.py @@ -1,5 +1,7 @@ import json import os +import sys +from io import StringIO from copy import deepcopy from github_automation.core.issue.issue import Issue @@ -630,3 +632,186 @@ def get_issue(*args, **kwargs): issue_object = manager.get_issue_object() assert issue_object is None + assert manager.run() is None + + +def test_loading_event_manager_with_closed_issue(): + + issue_id = "=asdf=sdf=" + title = "issue name" + labels = ["HighEffort", "Low", "bug", "test"] + assignee = "ronykoz" + issue = { + "projectCards": { + "nodes": [ + { + "id": "id=", + "column": { + "name": "testing" + }, + "project": { + "number": 1 + } + }, + { + "id": "id2=", + "column": { + "name": "Queue" + }, + "project": { + "number": 2 + } + } + ] + }, + "comments": { + "nodes": [ + { + "author": { + "login": "ronykoz" + }, + "body": "comment 1", + "createdAt": "2019-03-19T12:24:27Z" + }, + { + "author": { + "login": "ronykoz" + }, + "body": "second comment", + "createdAt": "2019-03-19T12:27:53Z" + }, + { + "author": { + "login": "ronykoz" + }, + "body": "third comment", + "createdAt": "2019-03-19T12:52:08Z" + } + ] + }, + "timelineItems": { + "__typename": "IssueTimelineItemsConnection", + "nodes": [ + { + "__typename": "LabeledEvent", + "label": { + "name": labels[0] + }, + "createdAt": "2019-03-15T12:40:22Z" + }, + { + "__typename": "LabeledEvent", + "label": { + "name": labels[1] + }, + "createdAt": "2019-03-17T13:59:27Z" + }, + { + "__typename": "LabeledEvent", + "label": { + "name": labels[2] + }, + "createdAt": "2019-04-08T10:48:02Z" + }, + { + "willCloseTarget": True, + "source": { + "__typename": "PullRequest", + "state": "OPEN", + "isDraft": False, + "assignees": { + "nodes": [ + { + "login": "test" + }, + { + "login": "test2" + } + ] + }, + "labels": { + "nodes": [ + { + "name": "label" + } + ] + }, + "reviewRequests": { + "totalCount": 0 + }, + "reviews": { + "totalCount": 3 + }, + "number": 1, + "reviewDecision": "APPROVED" + } + } + ] + }, + "title": title, + "id": issue_id, + "state": "closed", + "number": 1, + "milestone": { + "title": "test" + }, + "labels": { + "edges": [ + { + "node": { + "name": labels[0] + } + }, + { + "node": { + "name": labels[1] + } + }, + { + "node": { + "name": labels[2] + } + }, + { + "node": { + "name": labels[3] + } + } + ] + }, + "assignees": { + "edges": [ + { + "node": { + "login": assignee + } + } + ] + } + } + + event = { + "action": "some action", + "issue": { + "number": 1 + } + } + + class mock_client(object): + def get_issue(*args, **kwargs): + return { + "repository": { + "issue": issue + } + } + + client = mock_client() + saved_stdout = sys.stdout + manager = EventManager(os.path.join(MOCK_FOLDER_PATH, 'conf.ini'), client=client, event=json.dumps(event)) + + out = StringIO() + sys.stdout = out + + assert manager.run() is None + assert 'The issue is closed' in out.getvalue() + sys.stdout = saved_stdout