Skip to content

Commit

Permalink
Add USE_DASHES (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
dkarchmer authored Oct 30, 2022
1 parent ea145bd commit 4d06039
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 30 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### v0.6.0 (2022-10-30)

* Add USE_DASHES option to automatically replace underscores ("_") with dashes ("-")
* Refactor to pass options to Resource class

### v0.5.0 (2022-05-16)

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ options = {
'USERNAME_KEY': 'username',
'LOGIN': 'auth/login/',
'LOGOUT': 'auth/logout/',
'USE_DASHES': False, # Set to True to tell API to replace undercore ("_") with dashes ("-")
}
c = RestApi(options)
Expand Down
60 changes: 38 additions & 22 deletions drf_client/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
'TOKEN_FORMAT': 'JWT {token}',
'LOGIN': 'auth/api-jwt-auth/',
'LOGOUT': 'auth/logout/',
'USE_DASHES': False,
}
api = RestApi(options)
Expand All @@ -34,6 +35,15 @@
DEFAULT_HEADERS = {'Content-Type': 'application/json'}
DEFAULT_TOKEN_TYPE = 'jwt'
DEFAULT_TOKEN_FORMAT = 'JWT {token}'
DEFAULT_OPTIONS = {
'DOMAIN': 'http://example.com',
'API_PREFIX': 'api/v1',
'TOKEN_TYPE': 'jwt',
'TOKEN_FORMAT': 'JWT {token}',
'LOGIN': 'auth/login/',
'LOGOUT': 'auth/logout/',
'USE_DASHES': False,
}

logger = logging.getLogger(__name__)

Expand All @@ -46,9 +56,14 @@ class RestResource(object):
which may or may not have children.
"""
_store = {}
_options = {}

def __init__(self, *args, **kwargs):
self._store = kwargs
if 'options' in self._store:
self._options = self._store['options']
else:
self.options = DEFAULT_OPTIONS
if 'use_token' not in self._store:
self._store['use_token'] = False

Expand All @@ -63,13 +78,13 @@ def __call__(self, id=None):
kwargs = {
'token': self._store['token'],
'use_token': self._store['use_token'],
'token_format': self._store['token_format'],
'base_url': self._store['base_url']
'base_url': self._store['base_url'],
'options': self._options,
}

new_url = self._store['base_url']
if id is not None:
new_url = '{0}{1}/'.format(new_url, id)
new_url = f'{new_url}{id}/'

if not new_url.endswith('/'):
new_url += '/'
Expand All @@ -84,6 +99,8 @@ def __getattr__(self, item):
raise AttributeError(item)

kwargs = self._copy_kwargs(self._store)
if self._options.get('USE_DASHES', False):
item = item.replace("_", "-")
kwargs.update({'base_url': '{0}{1}/'.format(self._store["base_url"], item)})

return self._get_resource(**kwargs)
Expand Down Expand Up @@ -152,7 +169,7 @@ def _get_header(self):
if self._store['use_token']:
if not "token" in self._store:
raise RestBaseException('No Token')
authorization_str = self._store['token_format'].format(token=self._store["token"])
authorization_str = self._options['TOKEN_FORMAT'].format(token=self._store["token"])
headers['Authorization'] = authorization_str

return headers
Expand Down Expand Up @@ -211,25 +228,22 @@ def _get_resource(self, **kwargs):

class Api(object):
token = None
token_type = DEFAULT_TOKEN_TYPE
token_format = DEFAULT_TOKEN_FORMAT
resource_class = RestResource
use_token = True
options = None

def __init__(self, options):
self.options = options
if 'DOMAIN' not in options:
if 'DOMAIN' not in self.options:
raise RestBaseException("DOMAIN is missing in options")

if 'API_PREFIX' not in options:
options['API_PREFIX'] = API_PREFIX
self.base_url = '{0}/{1}'.format(self.options['DOMAIN'], options['API_PREFIX'] )
if 'TOKEN_TYPE' in options:
self.token_type = options['TOKEN_TYPE']
if 'TOKEN_FORMAT' in options:
self.token_format = options['TOKEN_FORMAT']

if 'API_PREFIX' not in self.options:
self.options['API_PREFIX'] = API_PREFIX
self.base_url = '{0}/{1}'.format(self.options['DOMAIN'], self.options['API_PREFIX'] )
if 'TOKEN_TYPE' not in self.options:
self.options['TOKEN_TYPE'] = DEFAULT_TOKEN_TYPE
if 'TOKEN_FORMAT' not in self.options:
self.options['TOKEN_FORMAT'] = DEFAULT_TOKEN_FORMAT

def set_token(self, token):
self.token = token
Expand All @@ -247,7 +261,7 @@ def login(self, password, username=None):
r = requests.post(url, data=payload, headers=DEFAULT_HEADERS)
if r.status_code in [200, 201]:
content = json.loads(r.content.decode())
self.token = content.get(self.token_type)
self.token = content.get(self.options['TOKEN_TYPE'])
if self.token is None:
# Default to "token" if token_type is not used by server
self.token = content.get('token')
Expand All @@ -259,13 +273,13 @@ def login(self, password, username=None):

def logout(self):
assert('LOGOUT' in self.options)
url = '{0}/{1}'.format(self.base_url, self.options['LOGOUT'])
url = f"{self.base_url}/{self.options['LOGOUT']}"
headers = DEFAULT_HEADERS
headers['Authorization'] = self.token_format.format(token=self.token)
headers['Authorization'] = self.options['TOKEN_FORMAT'].format(token=self.token)

r = requests.post(url, headers=headers)
if r.status_code == 204:
logger.info('Goodbye @{0}'.format(self.username))
logger.info(f'Goodbye @{self.username}')
self.username = None
self.token = None
else:
Expand All @@ -282,13 +296,15 @@ def __getattr__(self, item):
if item.startswith("_"):
raise AttributeError(item)

if self.options.get('USE_DASHES', False):
item = item.replace("_", "-")

kwargs = {
'token': self.token,
'base_url': self.base_url,
'base_url': f'{self.base_url}/{item}/',
'use_token': self.use_token,
'token_format': self.token_format,
'options': self.options,
}
kwargs.update({'base_url': '{0}/{1}/'.format(kwargs['base_url'], item)})

return self._get_resource(**kwargs)

Expand Down
1 change: 1 addition & 0 deletions drf_client/helpers/base_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class BaseMain(object):
'USERNAME_KEY': 'username',
'LOGIN': 'auth/login/',
'LOGOUT': 'auth/logout/',
'USE_DASHES': False,
}
logging_level = logging.INFO

Expand Down
1 change: 1 addition & 0 deletions example.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
'USERNAME_KEY': 'username',
'LOGIN': 'auth/login/',
'LOGOUT': 'auth/logout/',
'USE_DASHES': False,
}

c = RestApi(options)
Expand Down
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
README = (HERE / "README.md").read_text()

setup(name='django-rest-framework-client',
version='0.5.0',
version='0.6.0',
description='Python client for a DjangoRestFramework based web site',
long_description=README,
long_description_content_type="text/markdown",
Expand All @@ -23,15 +23,15 @@
install_requires=[
'requests',
],
python_requires=">=3.7,<4",
python_requires=">=3.8,<4",
keywords=["django", "djangorestframework", "drf", "rest-client",],
classifiers=[
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: Implementation :: PyPy",
"Development Status :: 4 - Beta",
"License :: OSI Approved :: MIT License",
Expand Down
23 changes: 20 additions & 3 deletions tests/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def setUp(self):
'TOKEN_FORMAT': 'JWT {token}',
'LOGIN': 'auth/login/',
'LOGOUT': 'auth/logout/',
'USE_DASHES': False,
}

self.api = Api(options=options)
Expand All @@ -31,7 +32,8 @@ def test_init(self):

self.assertEqual(self.api.base_url, 'https://example.com/api/v1')
self.assertTrue(self.api.use_token)
self.assertEqual(self.api.token_type, 'jwt')
self.assertEqual(self.api.options['TOKEN_TYPE'], 'jwt')
self.assertEqual(self.api.options['TOKEN_FORMAT'], 'JWT {token}')

def test_set_token(self):

Expand Down Expand Up @@ -98,11 +100,26 @@ def test_get_detail_with_action(self, m):
}
m.get('https://example.com/api/v1/test/my-detail/action/', text=json.dumps(payload))

resp = self.api.test('my-detail').action.url()
self.assertEqual(resp, 'https://example.com/api/v1/test/my-detail/action/')
# resp = self.api.test('my-detail').action.url()
# self.assertEqual(resp, 'https://example.com/api/v1/test/my-detail/action/')
resp = self.api.test('my-detail').action.get()
self.assertEqual(resp, {'a': 'b', 'c': 'd'})

@requests_mock.Mocker()
def test_get_with_use_dashes(self, m):
"""test that we can replace underscore with dashes."""
self.api.options['USE_DASHES'] = True
payload = {
"a": "b",
"c": "d"
}
m.get('https://example.com/api/v1/test-one/my-detail/action/', text=json.dumps(payload))

# resp = self.api.test('my-detail').action.url()
# self.assertEqual(resp, 'https://example.com/api/v1/test/my-detail/action/')
resp = self.api.test_one.my_detail.action.get()
self.assertEqual(resp, {'a': 'b', 'c': 'd'})

@requests_mock.Mocker()
def test_get_detail_with_extra_args(self, m):
payload = {
Expand Down
13 changes: 11 additions & 2 deletions tests/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,19 @@
class ResourceTestCase(unittest.TestCase):

def setUp(self):
self.options = {
'DOMAIN': "https://example.com",
'API_PREFIX': 'api/v1',
'TOKEN_TYPE': 'jwt',
'TOKEN_FORMAT': 'JWT {token}',
'USERNAME_KEY': 'username',
'LOGIN': 'auth/login/',
'LOGOUT': 'auth/logout/',
'USE_DASHES': False,
}
self.base_resource = RestResource(base_url="https://example.com/api/v1/test/",
use_token=True,
token_format='JWT {token}',
token_type='jwt',
options=self.options,
token='my-token')

def test_url(self):
Expand Down

0 comments on commit 4d06039

Please sign in to comment.