def test_removing_issues(): config = Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini')) config.load_properties() project = Project(name="test project", config=config, columns={ "Queue": ProjectColumn(id="id", name="Queue", cards=[ IssueCard(id="sdf", issue=Issue( id="sdf", title="title", number=1)) ]), "Review in progress": ProjectColumn(id="id", name="Review in progress", cards=[]) }) assert len(project.get_all_issue_ids()) == 1 assert len(project.columns.keys()) == 2 issue = Issue(id="1", number=1, title="1") issues = {1: issue} class ClientMock(object): def delete_project_card(self, **kwargs): return project.remove_issues(ClientMock, issues, config) assert project.get_all_issue_ids() == set()
def test_loading_configuration(): configuration = Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini'), quiet=True, log_path="/tmp/") assert 'General' in configuration.config.sections() configuration.load_properties() assert configuration.closed_issues_column == 'Done' assert configuration.project_owner == 'ronykoz' assert configuration.repository_name == 'test' assert configuration.project_number == 1 assert configuration.priority_list == [ 'Critical', 'High', 'Medium', 'Low', 'Customer|||zendesk' ] assert configuration.filter_labels == ['bug'] assert configuration.filter_milestone == '' assert configuration.must_have_labels == ['test'] assert configuration.cant_have_labels == ['not test'] assert configuration.column_names == [ 'Queue', 'In progress', 'Review in progress', 'Waiting for Docs' ] assert configuration.column_rule_desc_order == [ 'Queue', 'Waiting for Docs', 'Review in progress', 'In progress' ] assert configuration.remove is True assert configuration.add is True assert configuration.move is True assert configuration.sort is False assert configuration.column_to_rules['Waiting for Docs'][ 'issue.pull_request.review_requested'] is True assert configuration.column_to_rules['Waiting for Docs'][ 'issue.pull_request.assignees'] == ['ronykoz||not rony']
def test_missing_issues(): config = Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini')) config.load_properties() project = Project(name="test project", config=config, columns={ "Queue": ProjectColumn(id="id", name="Queue", cards=[ IssueCard(id="sdf", issue=Issue( id="sdf", title="title", number=1)) ]), "Review in progress": ProjectColumn(id="id", name="Review in progress", cards=[]) }) assert len(project.get_all_issue_ids()) == 1 assert len(project.columns.keys()) == 2 issue = Issue(id="2", number=2, title="issue title") issues = {"2": issue} assert project.find_missing_issue_ids(issues) == {"2"}
def test_adding_issue(): config = Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini')) config.load_properties() project = Project(name="test project", config=config, columns={ "Queue": ProjectColumn(id="id", name="Queue", cards=[ IssueCard(id="sdf", issue=Issue( id="sdf", title="title", number=2)) ]), "Review in progress": ProjectColumn(id="id", name="Review in progress", cards=[]) }) assert len(project.get_all_issue_ids()) == 1 assert len(project.columns.keys()) == 2 issue = Issue(id="1", number=1, title="Rony") assert issue.priority_rank == 0 issues = {"1": issue} class ClientMock(object): def add_issues_to_project(*args, **kwargs): return {'addProjectCard': {'cardEdge': {'node': {'id': "1"}}}} def add_to_column(*args, **kwargs): return def move_to_specific_place_in_column(*args, **kwargs): return project.add_issues(ClientMock, issues, {"1"}, config) assert project.columns['Queue'].cards[0].issue.title == "Rony" # testing non existent column with pytest.raises(Exception) as err: project.add_issue(ClientMock, issue, "non existent", config) assert "Did not found a matching column" in err issue2 = Issue(id="1", number=1, title="Rony", card_id_to_project={"1": { "project_number": 1 }}) issues2 = {"2": issue2} project.add_issues(ClientMock, issues2, {"2"}, config) assert project.columns['Queue'].cards[0].issue.title == "Rony"
def manage(**kwargs): """Manage a GitHub project board""" for conf_path in kwargs['conf'].split(','): configuration = Configuration(conf_file_path=conf_path, verbose=kwargs['verbose'], quiet=kwargs['quiet'], log_path=kwargs['log_path']) configuration.load_properties() manager = ProjectManager(configuration=configuration) manager.manage()
def test_get_matching_column(): config = Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini')) config.load_properties() issue_queue = Issue(id="1", title="issue 1", number=1) assert Project.get_matching_column(issue_queue, config) == 'Queue' issue_in_progress = Issue(id="1", title="issue 1", number=1) issue_in_progress.add_assignee("Rony") assert Project.get_matching_column(issue_in_progress, config) == '' issue_in_progress.add_label("Testing") assert Project.get_matching_column(issue_in_progress, config) == 'In progress' issue_review_in_progress = Issue(id="1", title="issue 1", number=1) issue_review_in_progress.add_assignee("Rony") class MockPullRequest(object): review_requested = True review_completed = False issue_review_in_progress.pull_request = MockPullRequest() assert Project.get_matching_column(issue_review_in_progress, config) == 'Review in progress' issue_docs = Issue(id="1", title="issue 1", number=1) issue_docs.add_assignee("Rony") class MockPullRequest2(object): review_requested = True review_completed = True assignees = "ronykoz" issue_docs.pull_request = MockPullRequest2() assert Project.get_matching_column(issue_docs, config) == 'Waiting for Docs' class MockPullRequest3(object): review_requested = True review_completed = True assignees = "someone" issue_docs.pull_request = MockPullRequest3() assert Project.get_matching_column(issue_docs, config) == 'Review in progress' # faulty field from issues config.column_to_rules["Waiting for Docs"] = { "issue.not_existent": "field" } assert Project.get_matching_column(issue_docs, config) == 'Review in progress'
def test_matching_issue_filter(): config = Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini')) config.load_properties() assert EventManager.is_matching_issue(['test'], config.must_have_labels, config.cant_have_labels) is True assert EventManager.is_matching_issue(['not test'], config.must_have_labels, config.cant_have_labels) is False assert EventManager.is_matching_issue(['not test', 'test'], config.must_have_labels, config.cant_have_labels) is False
def test_project_empty_card(): project = Project( name="test project", config=Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini')), columns={ "Queue": ProjectColumn(id="id", name="Queue", cards=[ IssueCard(id="sdf", issue=Issue(id="sdf", title="title", number=1)) ]), "Review in progress": ProjectColumn(id="id", name="Review in progress", cards=[]) }) assert project.name == "test project" assert len(project.get_all_issue_ids()) == 1 assert len(project.columns.keys()) == 2 assert project.columns['Queue'].name == 'Queue' assert project.columns['Review in progress'].name == 'Review in progress'
def test_no_card_content(): cards = _extract_card_node_data( {"cards": { "edges": [{ "node": { "no": "content" } }] }}, Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini'))) assert len(cards) == 0
def run(self): issue = self.get_issue_object() for conf_path in self.conf_paths: self.config = Configuration(conf_path, self.verbose, self.quiet, self.log_path) self.config.load_properties() if (self.config.project_number in issue.get_associated_project() or self.is_matching_issue(issue.labels, self.config.must_have_labels, self.config.cant_have_labels)): issue.set_priority(self.config.priority_list) self.manage_issue_in_project(issue) else: self.config.logger.debug( f"The issue does not match the filter provided in the configuration " f"file {conf_path}.")
def __init__(self, conf: str, event: str, quiet: bool = False, log_path: str = '', verbose: int = 2, client=None, api_key=None): self.config = None self.conf_paths = conf.split(',') self.quiet = quiet self.log_path = log_path self.verbose = verbose config = Configuration(self.conf_paths[0]) config.load_general_properties() self.project_owner = config.project_owner self.repository_name = config.repository_name self.event = json.loads(event) self.client = client if client else GraphQLClient(api_key)
def test_sort_column(): config = Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini')) config.load_properties() class MockClient(object): def add_to_column(*args, **kwargs): return def move_to_specific_place_in_column(*args, **kwargs): return mock_client = MockClient() # Sorting config.priority_list = DEFAULT_PRIORITY_LIST column_object = ProjectColumn(id="id", name="Review in progress", cards=[ IssueCard(id="sdf", issue=Issue(id="sdf", title="issue 2", number=2, labels=["Low"])), IssueCard(id="sdf3", issue=Issue(id="sdf", title="issue 4", number=4, labels=["Medium" ])), IssueCard(id="sdf2", issue=Issue(id="sdf2", title="issue 3", number=3, labels=["High"])) ]) column_object.sort_cards(mock_client, config) card_titles_in_column = [card.issue.title for card in column_object.cards] assert card_titles_in_column == ['issue 3', "issue 4", 'issue 2']
def test_event_manager_flow(mocker): config = Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini')) config.load_properties() project_object = Project(name="project_name", columns={ "In progress": ProjectColumn(id="some id", name='In progress', cards=[]) }, config=config) mocker.patch.object(EventManager, "get_issue_object", return_value=Issue(id="1", title="this is a test title", number=1, assignees=["ronykoz"], labels=['test', 'Testing'])) mocker.patch.object(EventManager, "load_project_column", return_value=project_object) class MockClient(object): def add_issues_to_project(*args, **kwargs): return {"addProjectCard": {"cardEdge": {"node": {"id": "1"}}}} def add_to_column(self, **kwargs): return def move_to_specific_place_in_column(self, **kwargs): return client = MockClient() manager = EventManager(os.path.join(MOCK_FOLDER_PATH, 'conf.ini'), client=client, event=json.dumps({"text": "text"})) manager.run() assert len(project_object.get_all_issue_ids()) == 1
def test_move_issues(): config = Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini')) config.load_properties() issue = Issue(id="1", title="title", number=1, assignees=["Rony"], labels=["Testing"], card_id_to_project={"sdf": { "project_number": 1 }}) project = Project(name="test project", config=config, columns={ "Queue": ProjectColumn( id="id", name="Queue", cards=[IssueCard(id="sdf", issue=issue)]), "In progress": ProjectColumn(id="id", name="In progress", cards=[]) }) class MockClient(object): def add_to_column(*args, **kwargs): return def move_to_specific_place_in_column(*args, **kwargs): return project.move_issues(MockClient(), {"1": issue}, config) 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(), {"1": issue}, config) assert project.is_in_column("Queue", "1") is False assert project.is_in_column("In progress", "1") is True
def test_get_prev_column(): event = {"action": "some action", "issue": {"number": 1}} project_layout = { "repository": { "project": { "columns": { "edges": [{ "cursor": 1, "node": { "name": "Queue" } }, { "cursor": 2, "node": { "name": "In progress" } }] } } } } class MockClient(object): def get_project_layout(*args, **kwargs): return project_layout client = MockClient() manager = EventManager(os.path.join(MOCK_FOLDER_PATH, 'conf.ini'), client=client, event=json.dumps(event)) manager.config = Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini')) manager.config.load_properties() assert manager.get_prev_column_cursor("Queue") == "" assert manager.get_prev_column_cursor("In progress") == 1
def test_loading(): project_layout = { "repository": { "project": { "columns": { "edges": [{ "cursor": 1, "node": { "name": "Queue" } }, { "cursor": 2, "node": { "name": "Review in progress" } }] } } } } column1 = { "repository": { "project": { "name": "test", "columns": { "nodes": [{ "name": "Queue", "id": "1234", "cards": { "pageInfo": { "hasNextPage": False, "endCursor": "MQ" }, "edges": [{ "cursor": "MQ", "node": { "note": None, "state": "CONTENT_ONLY", "id": "3434=", "content": { "id": "1234=", "number": 1, "title": "issue 1", "labels": { "edges": [{ "node": { "name": "High" } }, { "node": { "name": "bug" } }] }, "assignees": { "edges": [] } } } }] } }] } } } } column2 = { "repository": { "project": { "name": "test", "columns": { "nodes": [{ "name": "In progress", "id": "5656", "cards": { "pageInfo": { "hasNextPage": False, "endCursor": "MQ" }, "edges": [{ "cursor": "MQ", "node": { "note": None, "state": "CONTENT_ONLY", "id": "56565=", "content": { "id": "56567=", "number": 15, "title": "issue 2", "labels": { "edges": [{ "node": { "name": "Medium" } }, { "node": { "name": "bug" } }] }, "assignees": { "edges": [{ "node": { "id": "234", "login": "******" } }] } } } }, { "cursor": "MB", "node": { "note": None, "state": "CONTENT_ONLY", "id": "123=", "content": { "id": "1234=", "number": 3, "title": "issue 3", "labels": { "edges": [{ "node": { "name": "High" } }, { "node": { "name": "bug" } }] }, "assignees": { "edges": [{ "node": { "id": "234", "login": "******" } }] } } } }] } }] } } } } issue_id = "=asdf=sdf=" title = "issue name" labels = ["HighEffort", "Critical", "bug", "test", "Testing"] issue = { "projectCards": { "nodes": [{ "id": "id=", "project": { "number": 1, "columns": { "nodes": [{ "name": "testing" }] } } }, { "id": "id2=", "project": { "number": 2, "columns": { "nodes": [{ "name": "Queue" }] } } }] }, "comments": { "nodes": [{ "author": { "login": "******" }, "body": "comment 1", "createdAt": "2019-03-19T12:24:27Z" }, { "author": { "login": "******" }, "body": "second comment", "createdAt": "2019-03-19T12:27:53Z" }, { "author": { "login": "******" }, "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" }] }, "title": title, "id": issue_id, "number": 1, "milestone": { "title": "test" }, "labels": { "edges": [{ "node": { "name": labels[0] } }, { "node": { "name": labels[1] } }, { "node": { "name": labels[2] } }, { "node": { "name": labels[3] } }, { "node": { "name": labels[4] } }] }, "assignees": { "edges": [{ "node": { "login": "******" } }] } } issues = { "repository": { "issues": { "pageInfo": { "hasNextPage": True, "endCursor": "cursor" }, "edges": [{ "node": issue }] } } } issues_with_no_after = { "repository": { "issues": { "pageInfo": { "hasNextPage": False, "endCursor": "cursor" }, "edges": [{ "node": issue }] } } } config = Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini')) config.load_properties() config.sort = True class MockClient(object): def delete_project_card(*args, **kwargs): return def add_issues_to_project(self, **kwargs): return {"addProjectCard": {"cardEdge": {"node": {"id": "id="}}}} def get_project_layout(self, **kwargs): return project_layout def get_first_column_issues(self, **kwargs): return column1 def get_column_issues(self, **kwargs): return column2 def get_github_issues(self, **kwargs): if not kwargs['after']: return issues return issues_with_no_after def add_to_column(self, **kwargs): return def move_to_specific_place_in_column(self, **kwargs): return client = MockClient() manager = ProjectManager(configuration=config, client=client) assert len(manager.matching_issues) == 1 assert manager.project.name == 'test' assert manager.project.get_current_location("56567=") == ("In progress", "56565=") assert manager.project.get_current_location("Random text") == (None, None) assert manager.project.is_in_column("In progress", "56567=") is True assert manager.project.is_in_column("In progress", "Random text") is False manager.project.sort_issues_in_columns(client, config) issues = [ card.issue.title for card in manager.project.columns['In progress'].cards ] 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 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_load_project_column(): event = {"action": "some action", "issue": {"number": 1}} project_layout = { "repository": { "project": { "columns": { "edges": [{ "cursor": 1, "node": { "name": "Queue" } }, { "cursor": 2, "node": { "name": "In progress" } }] } } } } project_column1 = { "repository": { "project": { "columns": { "nodes": [{ "name": "Queue", "id": "id", "cards": { "pageInfo": { "endCursor": "A", "hasNextPage": True }, "edges": [{ "cursor": "A", "node": { "note": None, "state": "CONTENT_ONLY", "id": "id=", "content": { "id": "id=", "number": 1, "title": "title", "labels": { "edges": [{ "node": { "name": "one" } }, { "node": { "name": "two" } }, { "node": { "name": "three" } }] } } } }] }, }] } } } } project_column1_no_after = deepcopy(project_column1) project_column1_no_after['repository']['project']['columns']['nodes'][0][ 'cards']['pageInfo']['hasNextPage'] = False project_column2 = { "repository": { "project": { "columns": { "nodes": [{ "name": "In progress", "id": "id", "cards": { "pageInfo": { "endCursor": "B", "hasNextPage": True }, "edges": [{ "cursor": "B", "node": { "note": None, "state": "CONTENT_ONLY", "id": "cardid2=", "content": { "id": "id2=", "number": 2, "title": "title2", "labels": { "edges": [{ "node": { "name": "one" } }, { "node": { "name": "two" } }, { "node": { "name": "three" } }] } } } }] }, }] } } } } project_column2_no_after = deepcopy(project_column2) project_column2_no_after['repository']['project']['columns']['nodes'][0][ 'cards']['pageInfo']['hasNextPage'] = False issue_id = "=asdf=sdf=" title = "issue name" labels = ["test", "Low", "bug", "Testing"] assignee = "ronykoz" issue = { "repository": { "issue": { "projectCards": { "nodes": [{ "id": "idadded=", "project": { "number": 1, "columns": { "nodes": [{ "name": "testing" }] } } }, { "id": "id2=", "project": { "number": 2, "columns": { "nodes": [{ "name": "Queue" }] } } }] }, "comments": { "nodes": [{ "author": { "login": "******" }, "body": "comment 1", "createdAt": "2019-03-19T12:24:27Z" }, { "author": { "login": "******" }, "body": "second comment", "createdAt": "2019-03-19T12:27:53Z" }, { "author": { "login": "******" }, "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" }] }, "title": title, "id": issue_id, "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 } }] } } } } class MockClient(object): def add_issues_to_project(self, **kwargs): return { "addProjectCard": { "cardEdge": { "node": { "id": "idadded=" } } } } def add_to_column(self, **kwargs): return def move_to_specific_place_in_column(self, **kwargs): return def get_issue(*args, **kwargs): return issue def delete_project_card(*args, **kwargs): return def get_project_layout(*args, **kwargs): return project_layout def get_first_column_issues(*args, **kwargs): if 'start_cards_cursor' in kwargs: return project_column1_no_after return project_column1 def get_column_issues(*args, **kwargs): if 'start_cards_cursor' in kwargs: return project_column2_no_after return project_column2 client = MockClient() manager = EventManager(os.path.join(MOCK_FOLDER_PATH, 'conf.ini'), client=client, event=json.dumps(event)) manager.config = Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini')) manager.config.load_properties() project1 = manager.load_project_column("Queue") assert project1.columns["Queue"].name == "Queue" assert len(project1.columns["Queue"].cards) == 2 assert project1.columns["Queue"].cards[0].issue.title == "title" assert project1.columns["Queue"].cards[0].id == "id=" project2 = manager.load_project_column("In progress") assert project2.columns["In progress"].name == "In progress" assert len(project2.columns["In progress"].cards) == 2 assert project2.columns["In progress"].cards[0].issue.title == "title2" assert project2.columns["In progress"].get_card_id("id2=") == "cardid2="
def test_project(): project = Project(**parse_project( { "name": "test", "columns": { "nodes": [{ "name": "Queue", "id": "1234", "cards": { "pageInfo": { "hasNextPage": True, "endCursor": "MQ" }, "edges": [{ "cursor": "MQ", "node": { "note": None, "state": "CONTENT_ONLY", "id": "3434=", "content": { "id": "1234=", "number": 1, "title": "issue 1", "labels": { "edges": [{ "node": { "name": "High" } }, { "node": { "name": "bug" } }] }, "assignees": { "edges": [] } } } }] } }, { "name": "Review in progress", "id": "5656", "cards": { "pageInfo": { "hasNextPage": True, "endCursor": "MQ" }, "edges": [{ "cursor": "MQ", "node": { "note": None, "state": "CONTENT_ONLY", "id": "56565=", "content": { "id": "5656=", "number": 15, "title": "issue 2", "labels": { "edges": [{ "node": { "name": "Medium" } }, { "node": { "name": "bug" } }] }, "assignees": { "edges": [{ "node": { "id": "234", "login": "******" } }] } } } }] } }] } }, Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini')))) assert len(project.get_all_issue_ids()) == 2 assert len(project.columns.keys()) == 2 assert project.columns['Queue'].name == 'Queue' assert project.columns['Queue'].cards[0].issue_title == 'issue 1' assert project.columns['Review in progress'].name == 'Review in progress' assert project.columns['Review in progress'].cards[ 0].issue_title == 'issue 2'
def test_loading_illegal_configuration(): configuration = Configuration( os.path.join(MOCK_FOLDER_PATH, 'illegal_conf.ini')) with pytest.raises(ValueError) as exception: configuration.load_properties() assert 'You have either added a section which is not in the column_names key' in exception configuration = Configuration( os.path.join(MOCK_FOLDER_PATH, 'illegal_query_conf.ini')) with pytest.raises(ValueError) as exception: configuration.load_properties() assert 'You have entered an illegal query' in exception configuration = Configuration( os.path.join(MOCK_FOLDER_PATH, 'illegal_action_conf.ini')) with pytest.raises(ValueError) as exception: configuration.load_properties() assert 'Provided illegal key' in exception assert 'in Actions section' in exception configuration = Configuration( os.path.join(MOCK_FOLDER_PATH, 'illegal_general_conf.ini')) with pytest.raises(ValueError) as exception: configuration.load_properties() assert 'Provided illegal key' in exception assert 'in General section' in exception
def test_add_card_to_column(): config = Configuration(os.path.join(MOCK_FOLDER_PATH, 'conf.ini')) config.load_properties() config.priority_list = DEFAULT_PRIORITY_LIST column_object = ProjectColumn(id="id", name="Review in progress", cards=[ IssueCard(id="sdf", issue=Issue(id="sdf", title="issue 2", number=2, labels=["Low"])), IssueCard(id="sdf2", issue=Issue(id="sdf2", title="issue 3", number=3, labels=["High"])) ]) issue_to_inject = Issue(id="4", title="issue 4", number=4, labels=['Medium']) issue_to_inject.set_priority(DEFAULT_PRIORITY_LIST) class MockClient(object): def add_to_column(*args, **kwargs): return def move_to_specific_place_in_column(*args, **kwargs): return mock_client = MockClient() # Sorting column_object.sort_cards(mock_client, config) card_titles_in_column = [card.issue.title for card in column_object.cards] assert card_titles_in_column == ['issue 3', 'issue 2'] # Adding in the middle column_object.add_card("id", issue_to_inject, mock_client) card_titles_in_column = [card.issue.title for card in column_object.cards] assert card_titles_in_column == ['issue 3', 'issue 4', 'issue 2'] # Higher priority addition issue_to_inject2 = Issue(id="5", title="issue 5", number=5, labels=['Critical']) issue_to_inject2.set_priority(DEFAULT_PRIORITY_LIST) column_object.add_card("id", issue_to_inject2, mock_client) card_titles_in_column = [card.issue.title for card in column_object.cards] assert card_titles_in_column == [ 'issue 5', 'issue 3', 'issue 4', 'issue 2' ] # Lower priority addition issue_to_inject3 = Issue(id="6", title="issue 6", number=6) column_object.add_card("id", issue_to_inject3, mock_client) card_titles_in_column = [card.issue.title for card in column_object.cards] assert card_titles_in_column == [ 'issue 5', 'issue 3', 'issue 4', 'issue 2', "issue 6" ] # Same priority different number issue_to_inject4 = Issue( id="7", title="issue 7", number=-1, ) column_object.add_card("id", issue_to_inject4, mock_client) card_titles_in_column = [card.issue.title for card in column_object.cards] assert card_titles_in_column == [ 'issue 5', 'issue 3', 'issue 4', 'issue 2', "issue 7", "issue 6" ]
class EventManager(object): DEFAULT_PRIORITY_LIST = ['Critical', 'High', 'Medium', 'Low'] def __init__(self, conf: str, event: str, quiet: bool = False, log_path: str = '', verbose: int = 2, client=None, api_key=None): self.config = None self.conf_paths = conf.split(',') self.quiet = quiet self.log_path = log_path self.verbose = verbose config = Configuration(self.conf_paths[0]) config.load_general_properties() self.project_owner = config.project_owner self.repository_name = config.repository_name self.event = json.loads(event) self.client = client if client else GraphQLClient(api_key) @staticmethod def is_matching_issue(issue_labels, must_have_labels, cant_have_labels): for label in must_have_labels: if label not in issue_labels: return False for label in cant_have_labels: if label in issue_labels: return False return True @staticmethod def get_issue_number(event): return event['issue']['number'] def get_prev_column_cursor(self, column_name): layout = self.client.get_project_layout( owner=self.config.project_owner, repository_name=self.config.repository_name, project_number=self.config.project_number) prev_cursor = '' column_edges = layout['repository']['project']['columns']['edges'] for index, column in enumerate(column_edges): if column_name == column['node']['name']: if index != 0: prev_cursor = column_edges[index - 1]['cursor'] return prev_cursor def load_project_column(self, column_name): prev_cursor = self.get_prev_column_cursor(column_name) if prev_cursor: response = get_column_issues_with_prev_column( self.client, self.config, prev_cursor) else: response = get_first_column_issues(self.client, self.config) return Project( **parse_project(response.get("repository", {}).get('project', {}), config=self.config)) def manage_issue_in_project(self, issue): if (self.config.remove and self.config.project_number in issue.get_associated_project() and not self.is_matching_issue(issue.labels, self.config.must_have_labels, self.config.cant_have_labels)): card_id = [ _id for _id, value in issue.card_id_project.items() if value['project_number'] == self.config.project_number ][0] Project.remove_issue(self.client, issue.title, card_id, self.config) return matching_column_name = Project.get_matching_column(issue, self.config) project = self.load_project_column(matching_column_name) if self.config.add and self.config.project_number not in issue.get_associated_project( ): project.add_issue(self.client, issue, matching_column_name, self.config) return if (self.config.add and not all(project.get_current_location( issue.id)) # The issue is in the triage or self.config.move): column_name_before, _ = project.get_current_location(issue.id) if column_name_before != matching_column_name: project.move_issue(self.client, issue, matching_column_name, self.config) return if self.config.sort: column_name_before, _ = project.get_current_location(issue.id) if column_name_before == matching_column_name: project.columns[matching_column_name].sort_cards( self.client, self.config) return def get_issue_object(self): issue_number = self.get_issue_number(self.event) 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'])) return issue def run(self): issue = self.get_issue_object() for conf_path in self.conf_paths: self.config = Configuration(conf_path, self.verbose, self.quiet, self.log_path) self.config.load_properties() if (self.config.project_number in issue.get_associated_project() or self.is_matching_issue(issue.labels, self.config.must_have_labels, self.config.cant_have_labels)): issue.set_priority(self.config.priority_list) self.manage_issue_in_project(issue) else: self.config.logger.debug( f"The issue does not match the filter provided in the configuration " f"file {conf_path}.")