Check API response for additional pages of notifications

This commit is contained in:
Erik Johnson 2016-10-04 17:20:35 -05:00
parent 7dbcec5a09
commit 5b8ed2de2c

View File

@ -122,12 +122,14 @@ class Github(IntervalModule):
The below example enables desktop notifications, enables Pango hinting for The below example enables desktop notifications, enables Pango hinting for
differently-colored **update_error** and **refresh_icon** text, and alters differently-colored **update_error** and **refresh_icon** text, and alters
the both the status text and the colors used to visually denote the current the both the status text and the colors used to visually denote the current
status level. status level. It also sets the log level to debug, for troubleshooting
purposes.
.. code-block:: python .. code-block:: python
status.register( status.register(
'github', 'github',
log_level=logging.DEBUG,
notify_status=True, notify_status=True,
notify_unread=True, notify_unread=True,
access_token='0123456789abcdef0123456789abcdef01234567', access_token='0123456789abcdef0123456789abcdef01234567',
@ -146,6 +148,11 @@ class Github(IntervalModule):
}, },
) )
.. note::
Setting debug logging and authenticating with an access token will
include the access token in the log file, as the notification URL is
logged at this level.
.. _`GitHub Status API`: https://status.github.com/api .. _`GitHub Status API`: https://status.github.com/api
.. _`GitHub Status Dashboard`: https://status.github.com .. _`GitHub Status Dashboard`: https://status.github.com
.. _`notifications page`: https://github.com/notifications .. _`notifications page`: https://github.com/notifications
@ -465,7 +472,7 @@ class Github(IntervalModule):
# Auth not configured # Auth not configured
self.logger.debug( self.logger.debug(
'No auth configured, notifications will not be checked') 'No auth configured, notifications will not be checked')
return False return True
if not HAS_REQUESTS: if not HAS_REQUESTS:
self.logger.error( self.logger.error(
@ -473,28 +480,48 @@ class Github(IntervalModule):
self.failed_update = True self.failed_update = True
return False return False
try:
self.logger.debug( self.logger.debug(
'Making API request to retrieve unread notifications') 'Checking unread notifications using %s',
'access token' if self.access_token else 'username/password'
)
old_unread_url = None
if self.access_token: if self.access_token:
self.logger.debug('Authenticating using access_token') unread_url = ACCESS_TOKEN_AUTH_URL % self.access_token
response = requests.get(
ACCESS_TOKEN_AUTH_URL % self.access_token)
else: else:
self.logger.debug('Authenticating using username/password') unread_url = BASIC_AUTH_URL
response = requests.get(BASIC_AUTH_URL,
auth=(self.username, self.password)) self.current_unread = set()
self.logger.log(5, page_num = 0
while old_unread_url != unread_url:
old_unread_url = unread_url
page_num += 1
self.logger.debug(
'Reading page %d of notifications (%s)',
page_num, unread_url
)
try:
if self.access_token:
response = requests.get(unread_url)
else:
response = requests.get(
unread_url,
auth=(self.username, self.password)
)
self.logger.log(
5,
'Raw return from GitHub notification check: %s', 'Raw return from GitHub notification check: %s',
response.text) response.text)
unread_data = json.loads(response.text) unread_data = json.loads(response.text)
except (requests.ConnectionError, requests.Timeout) as exc: except (requests.ConnectionError, requests.Timeout) as exc:
self.logger.error('Failed to check unread notifications: %s', exc) self.logger.error(
'Failed to check unread notifications: %s', exc)
self.failed_update = True self.failed_update = True
return False return False
except json.decoder.JSONDecodeError as exc: except json.decoder.JSONDecodeError as exc:
self.logger.error('Error loading JSON: %s', exc) self.logger.error('Error loading JSON: %s', exc)
self.logger.debug('JSON text that failed to load: %s', response.text) self.logger.debug(
'JSON text that failed to load: %s', response.text)
self.failed_update = True self.failed_update = True
return False return False
@ -507,7 +534,60 @@ class Github(IntervalModule):
) )
) )
self.current_unread = set([x['id'] for x in unread_data if 'id' in x]) # Update the current count of unread notifications
self.current_unread.update(
[x['id'] for x in unread_data if 'id' in x]
)
# Check 'Link' header for next page of notifications
# (https://tools.ietf.org/html/rfc5988#section-5)
self.logger.debug('Checking for next page of notifications')
try:
link_header = response.headers['Link']
except AttributeError:
self.logger.error(
'No headers present in response. This might be due to '
'an API change in the requests module.'
)
self.failed_update = True
continue
except KeyError:
self.logger.debug('Only one page of notifications present')
continue
else:
# Process 'Link' header
try:
links = requests.utils.parse_header_links(link_header)
except Exception as exc:
self.logger.error(
'Failed to parse \'Link\' header: %s', exc
)
self.failed_update = True
continue
for link in links:
try:
link_rel = link['rel']
if link_rel != 'next':
# Link does not refer to the next page, skip it
continue
# Set the unread_url so that when we reach the top
# of the outer loop, we have a new URL to check.
unread_url = link['url']
break
except TypeError:
# Malformed hypermedia link
self.logger.warning(
'Malformed hypermedia link (%s) in \'Link\' '
'header (%s)', link, links
)
continue
else:
self.logger.debug('No more pages of notifications remain')
if self.failed_update:
return False
self.data['unread_count'] = len(self.current_unread) self.data['unread_count'] = len(self.current_unread)
self.data['unread'] = self.unread_marker \ self.data['unread'] = self.unread_marker \
if self.data['unread_count'] > 0 \ if self.data['unread_count'] > 0 \