Skip to content

Commit

Permalink
feat: DEPR EdxRestApiClient
Browse files Browse the repository at this point in the history
  • Loading branch information
Yagnesh committed Nov 6, 2023
1 parent b90ce80 commit 03731d1
Show file tree
Hide file tree
Showing 2 changed files with 4 additions and 157 deletions.
66 changes: 0 additions & 66 deletions edx_rest_api_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,69 +299,3 @@ def request(self, method, url, headers=None, **kwargs): # pylint: disable=argum
set_custom_attribute('api_client', 'OAuthAPIClient')
self._ensure_authentication()
return super().request(method, url, headers=headers, **kwargs)


class EdxRestApiClient(slumber.API):
"""
API client for edX REST API.
(deprecated) See docs/decisions/0002-oauth-api-client-replacement.rst.
"""

@classmethod
def user_agent(cls):
return USER_AGENT

@classmethod
def get_oauth_access_token(cls, url, client_id, client_secret, token_type='bearer',
timeout=(REQUEST_CONNECT_TIMEOUT, REQUEST_READ_TIMEOUT)):
# 'To help transition to OAuthAPIClient, use EdxRestApiClient.get_and_cache_jwt_oauth_access_token instead'
# 'of EdxRestApiClient.get_oauth_access_token to share cached jwt token used by OAuthAPIClient.'
return get_oauth_access_token(url, client_id, client_secret, token_type=token_type, timeout=timeout)

@classmethod
def get_and_cache_jwt_oauth_access_token(cls, url, client_id, client_secret,
timeout=(REQUEST_CONNECT_TIMEOUT, REQUEST_READ_TIMEOUT)):
return get_and_cache_oauth_access_token(url, client_id, client_secret, token_type="jwt", timeout=timeout)

def __init__(self, url, signing_key=None, username=None, full_name=None, email=None,
timeout=5, issuer=None, expires_in=30, tracking_context=None, oauth_access_token=None,
session=None, jwt=None, **kwargs):
"""
EdxRestApiClient is deprecated. Use OAuthAPIClient instead.
Instantiate a new client. You can pass extra kwargs to Slumber like
'append_slash'.
Raises:
ValueError: If a URL is not provided.
"""
set_custom_attribute('api_client', 'EdxRestApiClient')
if not url:
raise ValueError('An API url must be supplied!')

if jwt:
auth = SuppliedJwtAuth(jwt)
elif oauth_access_token:
auth = BearerAuth(oauth_access_token)
elif signing_key and username:
auth = JwtAuth(username, full_name, email, signing_key,
issuer=issuer, expires_in=expires_in, tracking_context=tracking_context)
else:
auth = None

session = session or requests.Session()
session.headers['User-Agent'] = self.user_agent()

session.timeout = timeout
super().__init__(
url,
session=session,
auth=auth,
**kwargs
)


EdxRestApiClient.user_agent.__func__.__doc__ = user_agent.__doc__
EdxRestApiClient.get_oauth_access_token.__func__.__doc__ = get_oauth_access_token.__doc__
95 changes: 4 additions & 91 deletions edx_rest_api_client/tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from edx_rest_api_client import __version__
from edx_rest_api_client.auth import JwtAuth
from edx_rest_api_client.client import (EdxRestApiClient, OAuthAPIClient, get_and_cache_oauth_access_token,
from edx_rest_api_client.client import (OAuthAPIClient, get_and_cache_oauth_access_token,
get_oauth_access_token, user_agent)
from edx_rest_api_client.tests.mixins import AuthenticationTestMixin

Expand All @@ -27,86 +27,6 @@
JWT = 'abc.123.doremi'


@ddt.ddt
class EdxRestApiClientTests(TestCase):
"""
Tests for the edX Rest API client.
"""

@ddt.unpack
@ddt.data(
({'url': URL, 'signing_key': SIGNING_KEY, 'username': USERNAME,
'full_name': FULL_NAME, 'email': EMAIL}, JwtAuth),
({'url': URL, 'signing_key': SIGNING_KEY, 'username': USERNAME, 'full_name': None, 'email': EMAIL}, JwtAuth),
({'url': URL, 'signing_key': SIGNING_KEY, 'username': USERNAME,
'full_name': FULL_NAME, 'email': None}, JwtAuth),
({'url': URL, 'signing_key': SIGNING_KEY, 'username': USERNAME, 'full_name': None, 'email': None}, JwtAuth),
({'url': URL, 'signing_key': SIGNING_KEY, 'username': USERNAME}, JwtAuth),
({'url': URL, 'signing_key': None, 'username': USERNAME}, type(None)),
({'url': URL, 'signing_key': SIGNING_KEY, 'username': None}, type(None)),
({'url': URL, 'signing_key': None, 'username': None, 'oauth_access_token': None}, type(None))
)
def test_valid_configuration(self, kwargs, auth_type):
"""
The constructor should return successfully if all arguments are valid.
We also check that the auth type of the api is what we expect.
"""
api = EdxRestApiClient(**kwargs)
self.assertIsInstance(api._store['session'].auth, auth_type) # pylint: disable=protected-access

@ddt.data(
{'url': None, 'signing_key': SIGNING_KEY, 'username': USERNAME},
{'url': None, 'signing_key': None, 'username': None, 'oauth_access_token': None},
)
def test_invalid_configuration(self, kwargs):
"""
If the constructor arguments are invalid, an InvalidConfigurationError should be raised.
"""
self.assertRaises(ValueError, EdxRestApiClient, **kwargs)

@mock.patch('edx_rest_api_client.auth.JwtAuth.__init__', return_value=None)
def test_tracking_context(self, mock_auth):
"""
Ensure the tracking context is included with API requests if specified.
"""
EdxRestApiClient(URL, SIGNING_KEY, USERNAME, FULL_NAME, EMAIL, tracking_context=TRACKING_CONTEXT)
self.assertIn(TRACKING_CONTEXT, mock_auth.call_args[1].values())

def test_oauth2(self):
"""
Ensure OAuth2 authentication is used when an access token is supplied to the constructor.
"""

with mock.patch('edx_rest_api_client.auth.BearerAuth.__init__', return_value=None) as mock_auth:
EdxRestApiClient(URL, oauth_access_token=ACCESS_TOKEN)
mock_auth.assert_called_with(ACCESS_TOKEN)

def test_supplied_jwt(self):
"""Ensure JWT authentication is used when a JWT is supplied to the constructor."""
with mock.patch('edx_rest_api_client.auth.SuppliedJwtAuth.__init__', return_value=None) as mock_auth:
EdxRestApiClient(URL, jwt=JWT)
mock_auth.assert_called_with(JWT)

def test_user_agent(self):
"""Make sure our custom User-Agent is getting built correctly."""
with mock.patch('socket.gethostbyname', return_value='test_hostname'):
default_user_agent = user_agent()
self.assertIn('python-requests', default_user_agent)
self.assertIn(f'edx-rest-api-client/{__version__}', default_user_agent)
self.assertIn('test_hostname', default_user_agent)

with mock.patch('socket.gethostbyname') as mock_gethostbyname:
mock_gethostbyname.side_effect = ValueError()
default_user_agent = user_agent()
self.assertIn('unknown_client_name', default_user_agent)

with mock.patch.dict(os.environ, {'EDX_REST_API_CLIENT_NAME': "awesome_app"}):
uagent = user_agent()
self.assertIn('awesome_app', uagent)

self.assertEqual(user_agent(), EdxRestApiClient.user_agent())


@ddt.ddt
class ClientCredentialTests(AuthenticationTestMixin, TestCase):
"""
Expand All @@ -126,10 +46,7 @@ def test_get_client_credential_access_token_success(self):

with freeze_time(now):
self._mock_auth_api(OAUTH_URL, code, body=body)
self.assertEqual(
EdxRestApiClient.get_oauth_access_token(OAUTH_URL, "client_id", "client_secret"),
expected_return
)
self.assertEqual(expected_return)

@ddt.data(
(400, {"error": "denied"}),
Expand All @@ -143,7 +60,6 @@ def test_get_client_credential_access_token_failure(self, code, body):
"""
with self.assertRaises(requests.RequestException):
self._mock_auth_api(OAUTH_URL, code, body=body)
EdxRestApiClient.get_oauth_access_token(OAUTH_URL, "client_id", "client_secret")

def test_refresh_token_required(self):
self._mock_auth_api(OAUTH_URL, 200, body=None)
Expand Down Expand Up @@ -171,17 +87,14 @@ def test_shared_client_credential_jwt_access_token(self):

with freeze_time(now):
self._mock_auth_api(OAUTH_URL, 200, body=body)
actual_return = EdxRestApiClient.get_and_cache_jwt_oauth_access_token(
OAUTH_URL, 'client_id', 'client_secret'
)
self.assertEqual(actual_return, expected_return)
self.assertEqual(expected_return)
self.assertEqual(len(responses.calls), 1)

# ensure OAuthAPIClient uses the same cached auth token without re-requesting the token from the server
oauth_client = OAuthAPIClient(OAUTH_URL, 'client_id', 'client_secret')
self._mock_auth_api(URL, 200, {'status': 'ok'})
oauth_client.post(URL, data={'test': 'ok'})
self.assertEqual(oauth_client.auth.token, actual_return[0])
self.assertEqual(oauth_client.auth.token)
self.assertEqual(len(responses.calls), 2)
self.assertEqual(URL, responses.calls[1][0].url)

Expand Down

0 comments on commit 03731d1

Please sign in to comment.