コード例 #1
0
def test_missing_items():
    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=[ItemCard(id="sdf",
                                                   item=Issue(id="sdf", title="title", number=1))]),
            "Review in progress": ProjectColumn(id="id", name="Review in progress",
                                                cards=[])
        }
    )

    assert len(project.get_all_item_ids()) == 1
    assert len(project.columns.keys()) == 2

    issue = Issue(
        id="2",
        number=2,
        title="issue title"
    )
    issues = {
        "2": issue
    }
    assert project.find_missing_item_ids(issues) == {"2"}
コード例 #2
0
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
コード例 #3
0
def test_removing_items():
    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=[ItemCard(id="sdf",
                                                   item=Issue(id="sdf", title="title", number=1)),
                                          ItemCard(id="sdff",
                                                   item=PullRequest(id="sdff", title="title", number=2))]),
            "Review in progress": ProjectColumn(id="id", name="Review in progress",
                                                cards=[])
        }
    )

    assert len(project.get_all_item_ids()) == 2
    assert len(project.columns.keys()) == 2

    class ClientMock(object):
        def delete_project_card(self, **kwargs):
            return

    project.remove_items(ClientMock, config)
    assert project.get_all_item_ids() == set()
コード例 #4
0
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=[
            ItemCard(id="sdf",
                     item=Issue(id="sdf", title="issue 2", number=2, labels=["Low"])),
            ItemCard(id="sdf3",
                     item=Issue(id="sdf", title="issue 4", number=4, labels=["Medium"])),
            ItemCard(id="sdf2",
                     item=PullRequest(id="sdf2", title="pull request 3", number=3, labels=["High"])),
        ])

    column_object.sort_cards(mock_client, config)
    card_titles_in_column = [card.item.title for card in column_object.cards]

    assert card_titles_in_column == ['pull request 3', "issue 4", 'issue 2']
コード例 #5
0
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']
コード例 #6
0
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"
コード例 #7
0
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'
コード例 #8
0
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()
        configuration.logger.info(f'Starting going over the board {conf_path}')
        manager = ProjectManager(configuration=configuration)
        manager.manage()
コード例 #9
0
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
コード例 #10
0
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', 'bug']))
    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
コード例 #11
0
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(), 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(), config)
    assert project.is_in_column("Queue", "1") is False
    assert project.is_in_column("In progress", "1") is True
コード例 #12
0
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)
コード例 #13
0
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)

    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,
            is_org_project=self.config.is_org_project)

        prev_cursor = ''
        project = get_project_from_response(layout, self.config.is_org_project)
        column_edges = project.get('columns', {}).get('edges',
                                                      {}) if project else {}
        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_items_with_prev_column(
                self.client, self.config, prev_cursor)

        else:
            response = get_first_column_items(self.client, self.config)

        project = get_project_from_response(response,
                                            self.config.is_org_project)
        return Project(**parse_project(project, config=self.config))

    def manage_item_in_project(self, item):
        if (self.config.remove and self.config.project_number
                in item.get_associated_project()
                and not is_matching_project_item(
                    item.labels, self.config.must_have_labels,
                    self.config.cant_have_labels, self.config.filter_labels)):

            card_id = [
                _id for _id, value in item.card_id_project.items()
                if value['project_number'] == self.config.project_number
            ][0]
            Project.remove_item(self.client, item.title, card_id, self.config,
                                item)
            return

        matching_column_name = Project.get_matching_column(item, self.config)

        if self.config.add and self.config.project_number not in item.get_associated_project(
        ):
            project = self.load_project_column(matching_column_name)
            project.add_item(self.client, item, matching_column_name,
                             self.config)
            return

        column_name_before = [
            value['project_column']
            for _id, value in item.card_id_project.items()
            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 {item.title} from {column_name_before}')
            project = self.load_project_column(matching_column_name)
            project.move_item(self.client, item, matching_column_name,
                              self.config)
            return

        if self.config.sort and column_name_before == matching_column_name:
            project = self.load_project_column(matching_column_name)
            project.columns[matching_column_name].sort_cards(
                self.client, self.config)

            return

    def get_project_item_object(self):
        """Get the issue or pull request full representation from the API"""
        if 'issue' in self.event:
            issue_number = self.event['issue']['number']
            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
        elif 'pull_request' in self.event:
            pr_number = self.event['pull_request']['number']
            pr_response = self.client.get_pull_request(self.project_owner,
                                                       self.repository_name,
                                                       pr_number)
            pr = PullRequest(
                **parse_pull_request(pr_response['repository']['pullRequest']))
            return pr
        else:
            print("This is not an issue or a pull request.")
            return

    def run(self):
        item = self.get_project_item_object()
        if item is None:
            return  # In case the event is not for an issue / pull request
        if item.state and item.state.upper() in ('CLOSED', 'MERGED'):
            print(f"The item is {item.state.lower()}, not taking an action.")
            return

        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 item.get_associated_project()
                    or is_matching_project_item(item.labels,
                                                self.config.must_have_labels,
                                                self.config.cant_have_labels,
                                                self.config.filter_labels)):
                item.set_priority(self.config.priority_list)
                self.manage_item_in_project(item)

            else:
                self.config.logger.debug(
                    f"The issue does not match the filter provided in the configuration "
                    f"file {conf_path}.")
コード例 #14
0
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=[
                                      ItemCard(id="sdf",
                                               item=Issue(id="sdf", title="issue 2", number=2, labels=["Low"])),
                                      ItemCard(id="sdf2",
                                               item=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.item.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.item.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.item.title for card in column_object.cards]
    assert card_titles_in_column == ['issue 5', 'issue 3', 'issue 4', 'issue 2']

    # Lower priority addition
    pr_to_inject3 = PullRequest(
        id="6",
        title="pull request 6",
        number=6
    )

    column_object.add_card("id", pr_to_inject3, mock_client)
    card_titles_in_column = [card.item.title for card in column_object.cards]
    assert card_titles_in_column == ['issue 5', 'issue 3', 'issue 4', 'issue 2', "pull request 6"]

    # Same priority different number
    pr_to_inject4 = PullRequest(
        id="7",
        title="pull request 7",
        number=-1,
    )

    column_object.add_card("id", pr_to_inject4, mock_client)
    card_titles_in_column = [card.item.title for card in column_object.cards]
    assert card_titles_in_column == ['issue 5', 'issue 3', 'issue 4', 'issue 2', "pull request 7", "pull request 6"]
コード例 #15
0
def test_move_items():
    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
                      }
                  }
                  )
    pull_request = PullRequest(id="10",
                               title="title",
                               number=10,
                               assignees=["Daud"],
                               labels=["Testing"],
                               card_id_to_project={
                                   "dsf": {
                                       "project_number": 1
                                   }
                               })
    project = Project(
        name="test project",
        config=config,
        columns={
            "Queue": ProjectColumn(id="id", name="Queue",
                                   cards=[ItemCard(id="sdf",
                                                   item=issue),
                                          ItemCard(id="dsf",
                                                   item=pull_request)
                                          ]),
            "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_items(MockClient(), config, {'1': issue})
    assert project.is_in_column("Queue", "1") is False
    assert project.is_in_column("In progress", "1") is True

    project.move_items(MockClient(), config, {'10': pull_request})
    assert project.is_in_column("Queue", "10") is False
    assert project.is_in_column("In progress", "10") is True

    # Move within the same column
    project.move_items(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_item(MockClient(), issue, 'In progress', config)
    assert project.is_in_column("In progress", "1") is True
コード例 #16
0
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 = {
        "search": {
            "pageInfo": {
                "hasNextPage": True,
                "endCursor": "cursor"
            },
            "edges": [{
                "node": issue
            }]
        }
    }
    issues_with_no_after = {
        "search": {
            "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 search_issues_by_query(self, **kwargs):
            if 'start_cursor' not in kwargs:
                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
コード例 #17
0
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 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 is_matching_issue(issue.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()
                       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)

        if self.config.add and self.config.project_number not in issue.get_associated_project():
            project = self.load_project_column(matching_column_name)
            project.add_issue(self.client, issue, matching_column_name, self.config)
            return

        column_name_before = [value['project_column'] for _id, value in issue.card_id_project.items()
                              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):
            project = self.load_project_column(matching_column_name)
            project.move_issue(self.client, issue, matching_column_name, self.config)
            return

        if self.config.sort and column_name_before == matching_column_name:
            project = self.load_project_column(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
                    is_matching_issue(issue.labels, self.config.must_have_labels, self.config.cant_have_labels,
                                      self.config.filter_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}.")
コード例 #18
0
def test_loading_repo_project():
    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": {
                                        "__typename": "Issue",
                                        "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": {
                                        "__typename": "Issue",
                                        "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": {
                                        "__typename": "Issue",
                                        "id": "1234=",
                                        "number": 3,
                                        "title": "issue 3",
                                        "labels": {
                                            "edges": [{
                                                "node": {
                                                    "name": "High"
                                                }
                                            }, {
                                                "node": {
                                                    "name": "bug"
                                                }
                                            }]
                                        },
                                        "assignees": {
                                            "edges": [{
                                                "node": {
                                                    "id": "234",
                                                    "login": "******"
                                                }
                                            }]
                                        }
                                    }
                                }
                            }, {
                                "cursor": "HZ",
                                "node": {
                                    "note": None,
                                    "state": "CONTENT_ONLY",
                                    "id": "76565=",
                                    "content": {
                                        "__typename": "PullRequest",
                                        "title": "pull_request 1",
                                        "id": "76566=",
                                        "state": "OPEN",
                                        "number": 20,
                                        "mergedAt": None,
                                        "merged": False,
                                        "reviewDecision": None,
                                        "reviews": {
                                            "totalCount": 0
                                        },
                                        "reviewRequests": {
                                            "totalCount": 0
                                        },
                                        "labels": {
                                            "edges": [{
                                                "node": {
                                                    "name": "Medium"
                                                }
                                            }, {
                                                "node": {
                                                    "name": "bug"
                                                }
                                            }]
                                        },
                                        "assignees": {
                                            "edges": [{
                                                "node": {
                                                    "id": "334",
                                                    "login": "******"
                                                }
                                            }]
                                        }
                                    }
                                }
                            }]
                        }
                    }]
                }
            }
        }
    }

    pr_id = "=zxcx=sads="
    pr_title = "pr name"
    labels = ["HighEffort", "Critical", "bug", "test", "Testing"]
    assignee = "daud"

    pull_request = {
        "projectCards": {
            "nodes": [{
                "id": "id=",
                "column": {
                    "name": "testing"
                },
                "project": {
                    "number": 1
                }
            }, {
                "id": "id2=",
                "column": {
                    "name": "Queue"
                },
                "project": {
                    "number": 2
                }
            }]
        },
        "title": pr_title,
        "id": pr_id,
        "number": 1,
        "state": "MERGED",
        "mergedAt": "2021-01-25T15:27:08Z",
        "merged": True,
        "reviewDecision": None,
        "reviews": {
            "totalCount": 0
        },
        "reviewRequests": {
            "totalCount": 1
        },
        "labels": {
            "edges": [{
                "node": {
                    "name": labels[0]
                }
            }, {
                "node": {
                    "name": labels[1]
                }
            }, {
                "node": {
                    "name": labels[2]
                }
            }, {
                "node": {
                    "name": labels[3]
                }
            }]
        },
        "assignees": {
            "edges": [{
                "node": {
                    "login": assignee
                }
            }]
        }
    }

    issue_id = "=asdf=sdf="
    issue_title = "issue name"
    issue = {
        "projectCards": {
            "nodes": [{
                "id": "id=",
                "column": {
                    "name": "Queue"
                },
                "project": {
                    "number": 1
                }
            }, {
                "id": "id2=",
                "column": {
                    "name": "In progress"
                },
                "project": {
                    "number": 2
                }
            }]
        },
        "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": issue_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": "******"
                }
            }]
        }
    }

    items = {
        "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_items(self, **kwargs):
            return column1

        def get_column_items(self, **kwargs):
            return column2

        def get_github_issues(self, **kwargs):
            if not kwargs['after']:
                return items

            return issues_with_no_after

        def add_to_column(self, **kwargs):
            return

        def move_to_specific_place_in_column(self, **kwargs):
            return

        def get_github_pull_requests(self, **kwargs):
            return {
                "repository": {
                    "pullRequests": {
                        "pageInfo": {
                            "endCursor": "cursor",
                            "hasNextPage": False
                        },
                        "edges": [{
                            "node": pull_request,
                        }]
                    }
                }
            }

    client = MockClient()
    manager = ProjectManager(configuration=config, client=client)

    assert len(manager.matching_issues) == 1
    assert len(manager.matching_pull_requests) == 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

    # assert there're 3 items 'In Progress' column
    manager.project.sort_items_in_columns(client, config)
    items = [
        card.item.title
        for card in manager.project.columns['In progress'].cards
    ]
    assert items == ['issue 3', 'issue 2', "pull_request 1"]

    # remove a single issue from the column
    manager.project.columns['In progress'].remove_card("56565=")
    items = [
        card.item.title
        for card in manager.project.columns['In progress'].cards
    ]
    assert len(items) == 2
    assert manager.project.is_in_column("In progress", "1234=") is True
    assert manager.project.is_in_column("In progress", "76566=") is True
    assert manager.project.is_in_column("In progress", "56565=") is False

    # remove a single pull request from the column
    manager.project.columns['In progress'].remove_card("76565=")
    items = [
        card.item.title
        for card in manager.project.columns['In progress'].cards
    ]
    assert len(items) == 1
    assert manager.project.is_in_column("In progress", "1234=") is True
    assert manager.project.is_in_column("In progress", "76566=") is False

    manager.manage()
    assert manager.project.is_in_column("In progress", "56565=") is False