Skip to content

Commit 01c6d9d

Browse files
committed
Oauth2ApiClient raise custom error
1 parent 019b357 commit 01c6d9d

3 files changed

Lines changed: 38 additions & 8 deletions

File tree

src/nypl_py_utils/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from .classes.kinesis_client import KinesisClient, KinesisClientError # noqa
33
from .classes.kms_client import KmsClient, KmsClientError # noqa
44
from .classes.mysql_client import MySQLClient, MySQLClientError # noqa
5-
from .classes.oauth2_api_client import Oauth2ApiClient # noqa
5+
from .classes.oauth2_api_client import Oauth2ApiClient, Oauth2ApiClientError # noqa
66
from .classes.postgresql_client import PostgreSQLClient, PostgreSQLClientError # noqa
77
from .classes.redshift_client import RedshiftClient, RedshiftClientError # noqa
88
from .classes.s3_client import S3Client, S3ClientError # noqa

src/nypl_py_utils/classes/oauth2_api_client.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,12 @@ def _do_http_method(self, method, request_path, **kwargs):
7171
except TokenExpiredError:
7272
self.logger.debug('TokenExpiredError encountered')
7373

74-
if '_do_http_method_token_refreshes' not in kwargs.keys():
75-
kwargs['_do_http_method_token_refreshes'] = 1
76-
else:
77-
kwargs['_do_http_method_token_refreshes'] += 1
78-
if kwargs['_do_http_method_token_refreshes'] > 3:
79-
raise Exception('Exhausted token refreshes')
74+
# Raise error after 3 successive token refreshes
75+
kwargs['_do_http_method_token_refreshes'] = \
76+
kwargs.get('_do_http_method_token_refreshes', 0) + 1
77+
if kwargs['_do_http_method_token_refreshes'] > 3:
78+
raise Oauth2ApiClientError('Exhausted token refreshes') \
79+
from None
8080

8181
self._generate_access_token()
8282
return self._do_http_method(method, request_path, **kwargs)
@@ -102,3 +102,8 @@ def _generate_access_token(self):
102102
client_id=self.client_id,
103103
client_secret=self.client_secret
104104
)
105+
106+
107+
class Oauth2ApiClientError(Exception):
108+
def __init__(self, message=None):
109+
self.message = message

tests/test_oauth2_api_client.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from requests_oauthlib import OAuth2Session
66
from requests import HTTPError
77

8-
from nypl_py_utils import Oauth2ApiClient
8+
from nypl_py_utils import (Oauth2ApiClient, Oauth2ApiClientError)
99

1010
_TOKEN_RESPONSE = {
1111
'access_token': 'super-secret-token',
@@ -110,3 +110,28 @@ def test_error_status_raises_error(self, requests_mock, test_instance,
110110

111111
with pytest.raises(HTTPError):
112112
test_instance._do_http_method('GET', 'foo')
113+
114+
def test_token_refresh_failure_raises_error(self, requests_mock,
115+
test_instance, token_server_post):
116+
"""
117+
Failure to fetch a token can raise a number of errors including:
118+
- requests.exceptions.HTTPError for invalid access_token
119+
- oauthlib.oauth2.rfc6749.errors.InvalidClientError for invalid
120+
credentials
121+
- oauthlib.oauth2.rfc6749.errors.MissingTokenError for failure to
122+
fetch a token
123+
One error that can arise from this client itself is failure to fetch
124+
a new valid token in response to token expiration. This test asserts
125+
that the client will not allow more than successive 3 retries.
126+
"""
127+
requests_mock.get(f'{BASE_URL}/foo', json={'foo': 'bar'})
128+
129+
token_response = dict(_TOKEN_RESPONSE)
130+
token_response['expires_in'] = 0
131+
token_server_post = requests_mock\
132+
.post(TOKEN_URL, text=json.dumps(token_response))
133+
134+
with pytest.raises(Oauth2ApiClientError):
135+
test_instance._do_http_method('GET', 'foo')
136+
# Expect 1 initial token fetch, plus 3 retries:
137+
assert len(token_server_post.request_history) == 4

0 commit comments

Comments
 (0)