Skip to content

Commit 0c9fd26

Browse files
authored
Merge pull request #13 from soda480/0.1.0
Add total method
2 parents 5b7e110 + fee75b7 commit 0c9fd26

File tree

4 files changed

+102
-2
lines changed

4 files changed

+102
-2
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ for page in client.get('/user/repos', _get='page'):
7171
print(repo['full_name'])
7272
```
7373

74+
`total` - Get total number of resources at given endpoint
75+
```python
76+
print(client.total('/user/repos'))
77+
6218
78+
```
79+
7480
### Projects using `github3api` ###
7581

7682
* [edgexfoundry/sync-github-labels](https://github.com/edgexfoundry/cd-management/tree/git-label-sync) A script that synchronizes GitHub labels and milestones
@@ -79,6 +85,10 @@ for page in client.get('/user/repos', _get='page'):
7985

8086
* [edgexfoundry/create-github-release](https://github.com/edgexfoundry/cd-management/tree/create-github-release) A script to facilitate creation of GitHub releases
8187

88+
* [soda480/prepbadge](https://github.com/soda480/prepbadge) A script that creates multiple pull request workflows to update a target organization repos with badges
89+
90+
* [soda480/github-contributions](https://github.com/soda480/github-contributions) A script to get contribution metrics for all members of a GitHub organization using the GitHub GraphQL API
91+
8292

8393
### Development ###
8494

build.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
authors = [Author('Emilio Reyes', 'emilio.reyes@intel.com')]
3131
summary = 'An advanced REST client for the GitHub API'
3232
url = 'https://github.com/soda480/github3api'
33-
version = '0.0.9'
33+
version = '0.1.0'
3434
default_task = [
3535
'clean',
3636
'analyze',

src/main/python/github3api/githubapi.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
HOSTNAME = 'api.github.com'
3030
VERSION = 'v3'
31+
DEFAULT_PAGE_SIZE = 30
3132

3233

3334
class GitHubAPI(RESTclient):
@@ -61,7 +62,7 @@ def _get_next_endpoint(self, url):
6162
if not url:
6263
logger.debug('link header is empty')
6364
return
64-
endpoint = url.replace(f'https://{self.hostname}', '')
65+
endpoint = self.get_endpoint_from_url(url)
6566
logger.debug(f'next endpoint is: {endpoint}')
6667
return endpoint
6768

@@ -112,6 +113,48 @@ def get(self, endpoint, **kwargs):
112113
else:
113114
return super(GitHubAPI, self).get(endpoint, **kwargs)
114115

116+
def total(self, endpoint):
117+
""" return total number of resources
118+
"""
119+
# logger.debug(f'get total number of resources at endpoint {endpoint}')
120+
response = self.get(endpoint, raw_response=True)
121+
if response.links:
122+
last_url = response.links['last']['url']
123+
endpoint = self.get_endpoint_from_url(last_url)
124+
items = self.get(endpoint)
125+
per_page = GitHubAPI.get_per_page_from_url(last_url)
126+
last_page = GitHubAPI.get_page_from_url(last_url)
127+
total = per_page * (last_page - 1) + len(items)
128+
else:
129+
items = response.json()
130+
total = len(items)
131+
return total
132+
133+
def get_endpoint_from_url(self, url):
134+
""" return endpoint from url
135+
"""
136+
return url.replace(f'https://{self.hostname}', '')
137+
138+
@staticmethod
139+
def get_page_from_url(url):
140+
""" get page query parameter form url
141+
"""
142+
regex = r'^.*page=(?P<value>\d+).*$'
143+
match = re.match(regex, url)
144+
if match:
145+
return int(match.group('value'))
146+
147+
@staticmethod
148+
def get_per_page_from_url(url):
149+
""" get per_page query parameter from url
150+
"""
151+
per_page = DEFAULT_PAGE_SIZE
152+
regex = r'^.*per_page=(?P<value>\d+).*$'
153+
match = re.match(regex, url)
154+
if match:
155+
per_page = int(match.group('value'))
156+
return per_page
157+
115158
@classmethod
116159
def get_client(cls):
117160
""" return instance of GitHubAPI

src/unittest/python/test_githubapi.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from mock import Mock
2121

2222
from github3api import GitHubAPI
23+
from github3api.githubapi import DEFAULT_PAGE_SIZE
2324

2425
from datetime import datetime
2526

@@ -345,3 +346,49 @@ def test__retry_chunkedencodingerror_error_Should_Return_False_When_NotChunkEnco
345346
def test__retry_chunkedencodingerror_error_Should_Return_True_When_ChunkEncodingError(self, *patches):
346347

347348
self.assertTrue(GitHubAPI._retry_chunkedencodingerror_error(ChunkedEncodingError()))
349+
350+
def test__get_endpoint_from_url_Should_ReturnExpected_When_Called(self, *patches):
351+
client = GitHubAPI(bearer_token='bearer-token')
352+
result = client.get_endpoint_from_url('https://api.github.com/user/repos?page=2')
353+
expected_result = '/user/repos?page=2'
354+
self.assertEqual(result, expected_result)
355+
356+
def test__get_page_from_url_Should_ReturnExpected_When_Match(self, *patches):
357+
result = GitHubAPI.get_page_from_url('https://api.github.com/user/repos?page=213')
358+
expected_result = 213
359+
self.assertEqual(result, expected_result)
360+
361+
def test__get_page_from_url_Should_ReturnExpected_When_NoMatch(self, *patches):
362+
result = GitHubAPI.get_page_from_url('https://api.github.com/user/repos')
363+
self.assertIsNone(result)
364+
365+
def test__get_per_page_from_url_Should_Return_Expected_When_Match(self, *patches):
366+
result = GitHubAPI.get_per_page_from_url('https://api.github.com/user/repos?page=213&per_page=75')
367+
expected_result = 75
368+
self.assertEqual(result, expected_result)
369+
370+
def test__get_per_page_from_url_Should_Return_Expected_When_NoMatch(self, *patches):
371+
result = GitHubAPI.get_per_page_from_url('https://api.github.com/user/repos?page=213')
372+
expected_result = DEFAULT_PAGE_SIZE
373+
self.assertEqual(result, expected_result)
374+
375+
@patch('github3api.GitHubAPI.get')
376+
def test__get_total_Should_ReturnExpected_When_NoLinks(self, get_patch, *patches):
377+
response_mock = Mock()
378+
response_mock.links = {}
379+
response_mock.json.return_value = ['', '', '']
380+
get_patch.return_value = response_mock
381+
client = GitHubAPI(bearer_token='bearer-token')
382+
result = client.total('/user/repos')
383+
expected_result = len(response_mock.json.return_value)
384+
self.assertEqual(result, expected_result)
385+
386+
@patch('github3api.GitHubAPI.get')
387+
def test__get_total_Should_ReturnExpected_When_Links(self, get_patch, *patches):
388+
response1_mock = Mock()
389+
response1_mock.links = {'next': {'url': 'https://api.github.com/user/repos?page=2', 'rel': 'next'}, 'last': {'url': 'https://api.github.com/user/repos?page=208', 'rel': 'last'}}
390+
get_patch.side_effect = [response1_mock, ['', '', '']]
391+
client = GitHubAPI(bearer_token='bearer-token')
392+
result = client.total('/user/repos')
393+
expected_result = DEFAULT_PAGE_SIZE * 207 + 3
394+
self.assertEqual(result, expected_result)

0 commit comments

Comments
 (0)