-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add: New GitHubRESTApi class for querying GitHub API
Add an API for querying the GitHub REST API
- Loading branch information
1 parent
6dae15e
commit e5abcae
Showing
4 changed files
with
321 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Copyright (C) 2022 Greenbone Networks GmbH | ||
# | ||
# SPDX-License-Identifier: GPL-3.0-or-later | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 3 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
# Copyright (C) 2022 Greenbone Networks GmbH | ||
# | ||
# SPDX-License-Identifier: GPL-3.0-or-later | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 3 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
from typing import Callable, Dict, Optional | ||
|
||
import requests | ||
|
||
DEFAULT_GITHUB_API_URL = "https://api.github.com" | ||
|
||
|
||
class GitHubRESTApi: | ||
def __init__( | ||
self, token: str, url: Optional[str] = DEFAULT_GITHUB_API_URL | ||
) -> None: | ||
self.token = token | ||
self.url = url | ||
|
||
def request( | ||
self, | ||
api: str, | ||
*, | ||
params: Optional[Dict[str, str]] = None, | ||
data: Optional[Dict[str, str]] = None, | ||
request: Optional[Callable] = None, | ||
) -> requests.Response: | ||
headers = { | ||
"Authorization": f"token {self.token}", | ||
"Accept": "application/vnd.github.v3+json", | ||
} | ||
request = request or requests.get | ||
return request( | ||
f"{self.url}{api}", headers=headers, params=params, json=data | ||
) | ||
|
||
def branch_exists(self, repo: str, branch: str) -> bool: | ||
""" | ||
Check if a single branch in a repository exists | ||
Args: | ||
repo: GitHub repository (owner/name) to use | ||
branch: Branch name to check | ||
""" | ||
api = f"/repos/{repo}/branches/{branch}" | ||
response = self.request(api) | ||
return response.ok | ||
|
||
def pull_request_commits( | ||
self, repo: str, pull_request: str | ||
) -> Dict[str, str]: | ||
""" | ||
Get all commit information of a pull request | ||
Hint: At maximum GitHub allows to receive 100 commits. If a pull request | ||
contains more then 100 commits only the first 100 are returned. | ||
Args: | ||
repo: GitHub repository (owner/name) to use | ||
pull_request: Pull request number | ||
Returns: | ||
Information about the commits in the pull request as a dict | ||
""" | ||
# per default github only shows 35 commits and at max it is only | ||
# possible to receive 100 | ||
params = {"per_page": "100"} | ||
api = f"/repos/{repo}/pulls/{pull_request}/commits" | ||
response = self.request(api, params=params) | ||
return response.json() | ||
|
||
def create_pull_request( | ||
self, | ||
repo: str, | ||
*, | ||
head_branch: str, | ||
base_branch: str, | ||
title: str, | ||
body: str, | ||
): | ||
""" | ||
Create a new Pull Request on GitHub | ||
Args: | ||
repo: GitHub repository (owner/name) to use | ||
head_branch: Branch to create a pull request from | ||
base_branch: Branch as as target for the pull | ||
title: Title for the pull request | ||
body: Description for the pull request. Can be formatted in Markdown | ||
Raises: | ||
HTTPError if the request was invalid | ||
""" | ||
api = f"/repos/{repo}/pulls" | ||
data = { | ||
"head": head_branch, | ||
"base": base_branch, | ||
"title": title, | ||
"body": body, | ||
} | ||
response = self.request(api, data=data, request=requests.post) | ||
response.raise_for_status() | ||
|
||
def add_pull_request_comment( | ||
self, repo: str, pull_request: str, comment: str | ||
): | ||
""" | ||
Add a comment to a pull request on GitHub | ||
Args: | ||
repo: GitHub repository (owner/name) to use | ||
pull_request: Pull request number where to add a comment | ||
comment: The actual comment message. Can be formatted in Markdown. | ||
Raises: | ||
HTTPError if the request was invalid | ||
""" | ||
api = f"/repos/{repo}/issues/{pull_request}/comments" | ||
data = {"body": comment} | ||
response = self.request(api, data=data, request=requests.post) | ||
response.raise_for_status() | ||
|
||
def delete_branch(self, repo: str, branch: str): | ||
""" | ||
Delete a branch on GitHub | ||
Args: | ||
repo: GitHub repository (owner/name) to use | ||
branch: Branch to be deleted | ||
Raises: | ||
HTTPError if the request was invalid | ||
""" | ||
api = f"/repos/{repo}/git/refs/{branch}" | ||
response = self.request(api, request=requests.delete) | ||
response.raise_for_status() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Copyright (C) 2022 Greenbone Networks GmbH | ||
# | ||
# SPDX-License-Identifier: GPL-3.0-or-later | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 3 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
# Copyright (C) 2022 Greenbone Networks GmbH | ||
# | ||
# SPDX-License-Identifier: GPL-3.0-or-later | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 3 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
import unittest | ||
|
||
from unittest.mock import patch, MagicMock | ||
|
||
from pontos.github.api import GitHubRESTApi | ||
|
||
|
||
class GitHubApiTestCase(unittest.TestCase): | ||
@patch("pontos.github.api.requests.get") | ||
def test_branch_exists(self, requests_mock: MagicMock): | ||
response = MagicMock() | ||
response.ok = True | ||
requests_mock.return_value = response | ||
|
||
api = GitHubRESTApi("12345") | ||
exists = api.branch_exists("foo/bar", "main") | ||
|
||
requests_mock.assert_called_once_with( | ||
'https://api.github.com/repos/foo/bar/branches/main', | ||
headers={ | ||
'Authorization': 'token 12345', | ||
'Accept': 'application/vnd.github.v3+json', | ||
}, | ||
params=None, | ||
json=None, | ||
) | ||
self.assertTrue(exists) | ||
|
||
@patch("pontos.github.api.requests.get") | ||
def test_branch_not_exists(self, requests_mock: MagicMock): | ||
response = MagicMock() | ||
response.ok = False | ||
requests_mock.return_value = response | ||
|
||
api = GitHubRESTApi("12345") | ||
exists = api.branch_exists("foo/bar", "main") | ||
|
||
requests_mock.assert_called_once_with( | ||
'https://api.github.com/repos/foo/bar/branches/main', | ||
headers={ | ||
'Authorization': 'token 12345', | ||
'Accept': 'application/vnd.github.v3+json', | ||
}, | ||
params=None, | ||
json=None, | ||
) | ||
self.assertFalse(exists) | ||
|
||
@patch("pontos.github.api.requests.get") | ||
def test_pull_request_commits(self, requests_mock: MagicMock): | ||
response = MagicMock() | ||
response.json.return_value = [{"sha": "1"}] | ||
requests_mock.return_value = response | ||
api = GitHubRESTApi("12345") | ||
commits = api.pull_request_commits("foo/bar", "1") | ||
|
||
requests_mock.assert_called_once_with( | ||
'https://api.github.com/repos/foo/bar/pulls/1/commits', | ||
headers={ | ||
'Authorization': 'token 12345', | ||
'Accept': 'application/vnd.github.v3+json', | ||
}, | ||
params={'per_page': '100'}, | ||
json=None, | ||
) | ||
|
||
self.assertEqual(len(commits), 1) | ||
self.assertEqual(commits[0]["sha"], "1") | ||
|
||
@patch("pontos.github.api.requests.post") | ||
def test_create_pull_request(self, requests_mock: MagicMock): | ||
api = GitHubRESTApi("12345") | ||
api.create_pull_request( | ||
"foo/bar", | ||
head_branch="foo", | ||
base_branch="main", | ||
title="Foo", | ||
body="This is bar", | ||
) | ||
|
||
requests_mock.assert_called_once_with( | ||
'https://api.github.com/repos/foo/bar/pulls', | ||
headers={ | ||
'Authorization': 'token 12345', | ||
'Accept': 'application/vnd.github.v3+json', | ||
}, | ||
params=None, | ||
json={ | ||
'head': 'foo', | ||
'base': 'main', | ||
'title': 'Foo', | ||
'body': 'This is bar', | ||
}, | ||
) | ||
|
||
@patch("pontos.github.api.requests.post") | ||
def test_add_pull_request_comment(self, requests_mock: MagicMock): | ||
api = GitHubRESTApi("12345") | ||
api.add_pull_request_comment("foo/bar", "123", "This is a comment") | ||
|
||
requests_mock.assert_called_once_with( | ||
'https://api.github.com/repos/foo/bar/issues/123/comments', | ||
headers={ | ||
'Authorization': 'token 12345', | ||
'Accept': 'application/vnd.github.v3+json', | ||
}, | ||
params=None, | ||
json={'body': 'This is a comment'}, | ||
) | ||
|
||
@patch("pontos.github.api.requests.delete") | ||
def test_delete_branch(self, requests_mock: MagicMock): | ||
api = GitHubRESTApi("12345") | ||
api.delete_branch("foo/bar", "foo") | ||
|
||
requests_mock.assert_called_once_with( | ||
'https://api.github.com/repos/foo/bar/git/refs/foo', | ||
headers={ | ||
'Authorization': 'token 12345', | ||
'Accept': 'application/vnd.github.v3+json', | ||
}, | ||
params=None, | ||
json=None, | ||
) |