Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-django-rest-framework-client
for openSUSE:Factory checked in at 2024-02-23 16:45:47
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-django-rest-framework-client (Old)
and
/work/SRC/openSUSE:Factory/.python-django-rest-framework-client.new.1770 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-django-rest-framework-client"
Fri Feb 23 16:45:47 2024 rev:5 rq:1149659 version:0.10.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-django-rest-framework-client/python-django-rest-framework-client.changes
2022-05-12 23:01:46.316910518 +0200
+++
/work/SRC/openSUSE:Factory/.python-django-rest-framework-client.new.1770/python-django-rest-framework-client.changes
2024-02-23 16:46:57.342874916 +0100
@@ -1,0 +2,30 @@
+Thu Feb 22 23:17:39 UTC 2024 - Steve Kowalik <[email protected]>
+
+- Update to 0.10.0:
+ * Add support to use a permanent Token.
+ * Fix bug using BaseFacade
+ * Fix bug using old Facade instead of BaseFacade
+ * Add isort and Black as formatter
+ * Add static BaseFacade class to allow access to API class and BaseMain
+ options
+ * Remove support for Python 3.8.
+ * Add set of `raw_*` methods that do not process results.
+ * Migrated to Python 3.10, Python 2 is not supported anymore
+ * Resource class methods respect additional `**kwargs` and `extra_headers`
+ parameters and pass them on to the underlying `requests` methods
+ * Fix to support `http://` schema in the server url
+ * Add USE_DASHES option to automatically replace underscores ("_") with
+ dashes ("-")
+ * Allow `delete()` method to accept optional `payload`
+ * Fix BaseMain Login method
+ * Add base_main helper
+ * Add method to be able to support resource names with "-" in the name
+ * Support Login based on usernames or email keys
+ * Drop support for Python 2. Test on v3.8 and v3.9
+ * Remove dependency on unitest2
+- Switch to autosetup and pyproject macros.
+- Drop patch python-django-rest-framework-client-no-unittest2.patch, no longer
+ required.
+- Refresh python-django-rest-framework-client-no-mock.patch
+
+-------------------------------------------------------------------
Old:
----
django-rest-framework-client-0.1.1.tar.gz
python-django-rest-framework-client-no-unittest2.patch
New:
----
django-rest-framework-client-0.10.0.tar.gz
BETA DEBUG BEGIN:
Old:- Switch to autosetup and pyproject macros.
- Drop patch python-django-rest-framework-client-no-unittest2.patch, no longer
required.
BETA DEBUG END:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-django-rest-framework-client.spec ++++++
--- /var/tmp/diff_new_pack.Qi76kk/_old 2024-02-23 16:46:57.942896674 +0100
+++ /var/tmp/diff_new_pack.Qi76kk/_new 2024-02-23 16:46:57.946896819 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-django-rest-framework-client
#
-# Copyright (c) 2022 SUSE LLC
+# Copyright (c) 2024 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -16,54 +16,43 @@
#
-%{?!python_module:%define python_module() python-%{**} python3-%{**}}
-%bcond_without python2
-%define skip_python36 1
+%define skip_python39 1
Name: python-django-rest-framework-client
-Version: 0.1.1
+Version: 0.10.0
Release: 0
Summary: Python client for a Django REST Framework based web site
License: MIT
-Group: Development/Languages/Python
URL: https://github.com/dkarchmer/django-rest-framework-client
-# newer versions exist on pypi, but without test packaged, see
-# https://github.com/dkarchmer/django-rest-framework-client/issues/7
Source:
https://github.com/dkarchmer/django-rest-framework-client/archive/v%{version}.tar.gz#/django-rest-framework-client-%{version}.tar.gz
-# fake dependency,
https://github.com/dkarchmer/django-rest-framework-client/pull/2
-Patch0: python-django-rest-framework-client-no-unittest2.patch
-# https://github.com/dkarchmer/django-rest-framework-client/issues/7
-Patch1: python-django-rest-framework-client-no-mock.patch
+Patch0: python-django-rest-framework-client-no-mock.patch
+BuildRequires: %{python_module base >= 3.10}
+BuildRequires: %{python_module pip}
BuildRequires: %{python_module setuptools}
+BuildRequires: %{python_module wheel}
BuildRequires: python-rpm-macros
# SECTION test requirements
BuildRequires: %{python_module Django}
BuildRequires: %{python_module pytest}
BuildRequires: %{python_module requests-mock}
BuildRequires: %{python_module requests}
-%if %{with python2}
-BuildRequires: python-unittest2
-%endif
# /SECTION
BuildRequires: fdupes
Requires: python-Django
Requires: python-requests
BuildArch: noarch
-
%python_subpackages
%description
Python client for a Django REST Framework based web site.
%prep
-%setup -q -n django-rest-framework-client-%{version}
-%patch0 -p1
-%patch1 -p1
+%autosetup -p1 -n django-rest-framework-client-%{version}
%build
-%python_build
+%pyproject_wheel
%install
-%python_install
+%pyproject_install
%python_expand %fdupes %{buildroot}%{$python_sitelib}
%check
@@ -72,5 +61,6 @@
%files %{python_files}
%doc CHANGELOG.md README.md
%license LICENSE
-%{python_sitelib}/*
+%{python_sitelib}/drf_client
+%{python_sitelib}/django_rest_framework_client-%{version}.dist-info
++++++ django-rest-framework-client-0.1.1.tar.gz ->
django-rest-framework-client-0.10.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-rest-framework-client-0.1.1/.github/workflows/python-publish.yml
new/django-rest-framework-client-0.10.0/.github/workflows/python-publish.yml
--- old/django-rest-framework-client-0.1.1/.github/workflows/python-publish.yml
1970-01-01 01:00:00.000000000 +0100
+++
new/django-rest-framework-client-0.10.0/.github/workflows/python-publish.yml
2023-10-05 03:44:46.000000000 +0200
@@ -0,0 +1,31 @@
+# This workflows will upload a Python Package using Twine when a release is
created
+# For more information see:
https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
+
+name: Upload Python Package
+
+on:
+ release:
+ types: [created]
+
+jobs:
+ deploy:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: '3.10'
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install setuptools wheel twine
+ - name: Build and publish
+ env:
+ TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
+ TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+ run: |
+ python setup.py sdist bdist_wheel
+ twine upload dist/*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-rest-framework-client-0.1.1/.travis.yml
new/django-rest-framework-client-0.10.0/.travis.yml
--- old/django-rest-framework-client-0.1.1/.travis.yml 2017-05-07
21:11:26.000000000 +0200
+++ new/django-rest-framework-client-0.10.0/.travis.yml 2023-10-05
03:44:46.000000000 +0200
@@ -1,9 +1,8 @@
language: python
sudo: false
python:
-- '2.7'
-- '3.4'
-- '3.6'
+- '3.9'
+- '3.10'
install:
- pip install --upgrade tox-travis -r requirements-build.txt -r
requirements-test.txt
script:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-rest-framework-client-0.1.1/CHANGELOG.md
new/django-rest-framework-client-0.10.0/CHANGELOG.md
--- old/django-rest-framework-client-0.1.1/CHANGELOG.md 2017-05-07
21:11:26.000000000 +0200
+++ new/django-rest-framework-client-0.10.0/CHANGELOG.md 2023-10-05
03:44:46.000000000 +0200
@@ -1,3 +1,60 @@
+### v0.10.0 (2023-10-04)
+
+ * Add support to use a permanent Token.
+
+### v0.9.2 (2023-08-25)
+
+ * Fix bug using BaseFacade
+
+### v0.9.1 (2023-08-20)
+
+ * Fix bug using old Facade instead of BaseFacade
+
+### v0.9.0 (2023-08-20)
+
+ * Add isort and Black as formatter
+ * Add static BaseFacade class to allow access to API class and BaseMain
options
+
+### v0.8.0 (2023-07-04)
+
+ * Remove support for Python 3.8.
+ * Add set of `raw_*` methods that do not process results.
+
+### v0.7.0 (2023-05-08)
+
+ * Migrated to Python 3.10, Python 2 is not supported anymore
+ * Resource class methods respect additional `**kwargs` and `extra_headers`
parameters and pass them on to the underlying `requests` methods
+ * Fix to support `http://` schema in the server url
+
+### 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)
+
+ * Allow `delete()` method to accept optional `payload`
+
+### v0.4.1 (2022-03-13)
+
+ * Fix BaseMain Login method
+ * Fix PYPI error
+
+### v0.3.0 (2022-03-13)
+
+ * Add missing PYPI long description
+ * Add base_main helper
+
+### v0.2.0 (2022-03-09)
+
+ * Add method to be able to support resource names with "-" in the name
+ * Support Login based on usernames or email keys
+ * Drop support for Python 2. Test on v3.8 and v3.9
+
+### v0.1.2 (2020-06-01)
+
+ * Remove dependency on unitest2
+
### v0.1.0 (2017-05-06)
- * First release
\ No newline at end of file
+ * First release
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-rest-framework-client-0.1.1/README.md
new/django-rest-framework-client-0.10.0/README.md
--- old/django-rest-framework-client-0.1.1/README.md 2017-05-07
21:11:26.000000000 +0200
+++ new/django-rest-framework-client-0.10.0/README.md 2023-10-05
03:44:46.000000000 +0200
@@ -1,7 +1,6 @@
-# Strato Python API Package
+# Django Rest Framework Python API Package
-[](https://travis-ci.org/dkarchmer/django-rest-framework-client)
-[](https://pypi.python.org/pypi/django-rest-framework-client)
+[](https://pypi.python.org/pypi/django-rest-framework-client)
A python library for interacting with any Django web server base on
django-rest-framework
@@ -21,12 +20,14 @@
restframeworkclient requires the following modules.
- * Python 2.7+ or 3.4+
+ * Python 3.9+
* requests
## Installation
-```
+```bash
+python3 -m venv ~/.virtualenv/drf_client
+source ~/.virtualenv/drf_client/bin/activate
pip install django-rest-framework-client
```
@@ -42,13 +43,15 @@
'API_PREFIX': 'api/v1',
'TOKEN_TYPE': 'jwt',
'TOKEN_FORMAT': 'JWT {token}',
+ '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)
-ok = c.login(email=email, password=password)
+ok = c.login(username=username, password=password)
if ok:
# GET some data
@@ -64,8 +67,33 @@
resp = c.myresourcename.post(data=payload)
+ # If the URL includes "-", add under parenthesis:
+ # GET: /api/v1/someresource/some-path/
+ my_object = c.someresource('some-path').get()
+
+```
+
+### Example using Tokens
+
```
+from drf_client.helpers.base_main import BaseMain
+class MyClass(Main):
+
+ options = {
+ 'DOMAIN': None,
+ 'API_PREFIX': 'api/v1',
+ 'TOKEN_TYPE': 'bearer',
+ 'TOKEN_FORMAT': 'Bearer {token}',
+ 'USERNAME_KEY': 'username',
+ 'LOGIN': 'auth/login/',
+ 'LOGOUT': 'auth/logout/',
+ 'USE_DASHES': False,
+ }
+
+export DRF_CLIENT_AUTH_TOKEN=1fe171f65917db0072abc6880196989dd2a20025
+python -m my_script.MyClass --server https://mysite.com --use-token t
+```
## Django Setup
@@ -95,17 +123,104 @@
```
-## Development
+## Helpers
-To test, run python setup.py test or to run coverage analysis:
+### BaseMain Helper
+
+This class helps write a script with a flexible template that helps avoid
having to duplicate
+boiler plate code from script to script.
+
+The class assumes that most scripts include the basic folliwing flow:
```
-coverage run --source=iotile_cloud setup.py test
-coverage report -m
+# Parse arguments
+# Setup LOG configuration
+# Login
+# Do something after logging in
```
-You can also use py.test:
+The opinionated class will execute the basic main flow:
+```python
+ # Initialize arguments and LOG in the init function
+ # Add additional arguments by implemenenting self.add_extra_args()
+ self.domain = self.get_domain()
+ self.api = Api(self.domain)
+ self.before_login()
+ ok = self.login()
+ if ok:
+ self.after_login()
```
+
+Any of the above functions can be overwritten by derving from this class.
+
+Here is a sample script:
+
+```python
+from drf_client.helper.base_main import BaseMain
+from drf_client.helper.base_facade import BaseFacade
+
+class MyScript(BaseMain):
+
+ def add_extra_args(self):
+ # Add extra positional argument (as example)
+ self.parser.add_argument('foo', metavar='foo', type=str, help='RTFM')
+
+ def before_login(self):
+ logger.info('-----------')
+
+ def after_login(self):
+ # Main function to OVERWITE and do real work
+ resp = self.api.foo.bar.get()
+ # You can also access the API from the global Facade
+ resp = BaseFacade.api.foo.bar.get()
+
+
+if __name__ == '__main__':
+
+ work = MyScript()
+ work.main()
+```
+
+Given the above script, you will run it with
+
+```bash
+python myscript.py -u <USERNAME> --foo bar
+```
+
+## Development
+
+To test, run python setup.py test or to run coverage analysis:
+
+```bash
+python3 -m venv .virtualenv/drf_client
+source .virtualenv/drf_client/bin/activate
+pip install -r requirements-test.txt
+pip install -e .
+
py.test
```
+
+## CI Deployment
+
+1. Update `setup.py` with new version
+2. Update `CHANGELOG.md` with description of new version
+2. Create new tag with same version
+
+```
+git tag v0.4.1 -m "v0.4.1"
+git push --tags
+```
+
+3. Create new release using GitHub Web Site. Github action will run
automatically to deploy to PyPi.
+
+## Manual Deployment
+
+```bash
+pip install -r requirements-build.txt
+
+python setup.py sdist bdist_wheel
+twine check dist/*
+# Publish
+twine upload dist/*
+```
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-rest-framework-client-0.1.1/drf_client/connection.py
new/django-rest-framework-client-0.10.0/drf_client/connection.py
--- old/django-rest-framework-client-0.1.1/drf_client/connection.py
2017-05-07 21:11:26.000000000 +0200
+++ new/django-rest-framework-client-0.10.0/drf_client/connection.py
2023-10-05 03:44:46.000000000 +0200
@@ -1,5 +1,3 @@
-__author__ = 'dkarchmer'
-
"""
See https://gist.github.com/dkarchmer/d85e55f9ed5450ba58cb
This API generically supports DjangoRestFramework based APIs
@@ -15,6 +13,7 @@
'TOKEN_FORMAT': 'JWT {token}',
'LOGIN': 'auth/api-jwt-auth/',
'LOGOUT': 'auth/logout/',
+ 'USE_DASHES': False,
}
api = RestApi(options)
@@ -25,32 +24,48 @@
api.logout()
"""
import json
-import requests
import logging
-import os
+
+import requests
+
from .exceptions import *
-API_PREFIX = 'api/v1'
-DEFAULT_HEADERS = {'Content-Type': 'application/json'}
-DEFAULT_TOKEN_TYPE = 'jwt'
-DEFAULT_TOKEN_FORMAT = 'JWT {token}'
+API_PREFIX = "api/v1"
+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__)
-class RestResource(object):
+class RestResource:
"""
Resource provides the main functionality behind a Django Rest Framework
based API. It handles the
attribute -> url, kwarg -> query param, and other related behind the scenes
python to HTTP transformations. It's goal is to represent a single resource
which may or may not have children.
"""
+
_store = {}
+ _options = {}
def __init__(self, *args, **kwargs):
self._store = kwargs
- if 'use_token' not in self._store:
- self._store['use_token'] = False
+ 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
def __call__(self, id=None):
"""
@@ -61,20 +76,20 @@
"""
kwargs = {
- 'token': self._store['token'],
- 'use_token': self._store['use_token'],
- 'token_format': self._store['token_format'],
- 'base_url': self._store['base_url']
+ "token": self._store["token"],
+ "use_token": self._store["use_token"],
+ "base_url": self._store["base_url"],
+ "options": self._options,
}
- new_url = self._store['base_url']
+ 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 += '/'
+ if not new_url.endswith("/"):
+ new_url += "/"
- kwargs['base_url'] = new_url
+ kwargs["base_url"] = new_url
return self._get_resource(**kwargs)
@@ -84,7 +99,9 @@
raise AttributeError(item)
kwargs = self._copy_kwargs(self._store)
- kwargs.update({'base_url': '{0}{1}/'.format(self._store["base_url"],
item)})
+ 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)
@@ -105,12 +122,21 @@
return d.items()
def _check_for_errors(self, resp, url):
-
if 400 <= resp.status_code <= 499:
- exception_class = HttpNotFoundError if resp.status_code == 404
else HttpClientError
- raise exception_class("Client Error %s: %s" % (resp.status_code,
url), response=resp, content=resp.content)
+ exception_class = (
+ HttpNotFoundError if resp.status_code == 404 else
HttpClientError
+ )
+ raise exception_class(
+ "Client Error %s: %s" % (resp.status_code, url),
+ response=resp,
+ content=resp.content,
+ )
elif 500 <= resp.status_code <= 599:
- raise HttpServerError("Server Error %s: %s" % (resp.status_code,
url), response=resp, content=resp.content)
+ raise HttpServerError(
+ "Server Error %s: %s" % (resp.status_code, url),
+ response=resp,
+ content=resp.content,
+ )
def _handle_redirect(self, resp, **kwargs):
# @@@ Hacky, see description in __call__
@@ -133,7 +159,6 @@
return resp.content
def _process_response(self, resp):
-
self._check_for_errors(resp, self.url())
if 200 <= resp.status_code <= 299:
@@ -144,55 +169,101 @@
def url(self, args=None):
url = self._store["base_url"]
if args:
- url += '?{0}'.format(args)
+ url += "?{0}".format(args)
return url
- def _get_header(self):
+ def _get_headers(self):
headers = DEFAULT_HEADERS
- if self._store['use_token']:
+ 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"])
- headers['Authorization'] = authorization_str
+ raise RestBaseException("No Token")
+ authorization_str = self._options["TOKEN_FORMAT"].format(
+ token=self._store["token"]
+ )
+ headers["Authorization"] = authorization_str
return headers
- def get(self, **kwargs):
+ def raw_get(self, extra_headers: dict = None, **kwargs):
+ """Call get and return raw request respond."""
args = None
- if 'extra' in kwargs:
- args = kwargs['extra']
- resp = requests.get(self.url(args), headers=self._get_header())
+ if "extra" in kwargs:
+ args = kwargs["extra"]
+ headers = (
+ self._get_headers() | extra_headers
+ if extra_headers
+ else self._get_headers()
+ )
+
+ return requests.get(self.url(args), headers=headers)
+
+ def get(self, extra_headers: dict = None, **kwargs):
+ """Call get and process respond."""
+ resp = self.raw_get(extra_headers, **kwargs)
return self._process_response(resp)
- def post(self, data=None, **kwargs):
- if data:
- payload = json.dumps(data)
- else:
- payload = None
-
- resp = requests.post(self.url(), data=payload,
headers=self._get_header())
+ def raw_post(self, data: dict = None, extra_headers: dict = None,
**kwargs):
+ """Call requests post and return raw respond."""
+ payload = json.dumps(data) if data and "files" not in kwargs else data
+ headers = (
+ self._get_headers() | extra_headers
+ if extra_headers
+ else self._get_headers()
+ )
+
+ return requests.post(self.url(), data=payload, headers=headers,
**kwargs)
+
+ def post(self, data: dict = None, extra_headers: dict = None, **kwargs):
+ """Call post and process respond."""
+ resp = self.raw_post(data, extra_headers, **kwargs)
return self._process_response(resp)
- def patch(self, data=None, **kwargs):
- if data:
- payload = json.dumps(data)
- else:
- payload = None
-
- resp = requests.patch(self.url(), data=payload,
headers=self._get_header())
+ def raw_patch(self, data=None, extra_headers: dict = None, **kwargs):
+ """Call patch and return raw request respond."""
+ payload = json.dumps(data) if data and "files" not in kwargs else data
+ headers = (
+ self._get_headers() | extra_headers
+ if extra_headers
+ else self._get_headers()
+ )
+
+ return requests.patch(self.url(), data=payload, headers=headers,
**kwargs)
+
+ def patch(self, data=None, extra_headers: dict = None, **kwargs):
+ """Call patch and process respond."""
+ resp = self.raw_patch(data, extra_headers, **kwargs)
return self._process_response(resp)
- def put(self, data=None, **kwargs):
- if data:
- payload = json.dumps(data)
- else:
- payload = None
-
- resp = requests.put(self.url(), data=payload,
headers=self._get_header())
+ def raw_put(self, data=None, extra_headers: dict = None, **kwargs):
+ """Call Put and return raw request respond."""
+ payload = json.dumps(data) if data and "files" not in kwargs else data
+ headers = (
+ self._get_headers() | extra_headers
+ if extra_headers
+ else self._get_headers()
+ )
+
+ return requests.put(self.url(), data=payload, headers=headers,
**kwargs)
+
+ def put(self, data=None, extra_headers: dict = None, **kwargs):
+ """Call Put and process respond."""
+ resp = self.raw_put(data, extra_headers, **kwargs)
return self._process_response(resp)
- def delete(self, **kwargs):
- resp = requests.delete(self.url(), headers=self._get_header())
+ def raw_delete(self, data=None, extra_headers: dict = None, **kwargs):
+ """Call Delete and return raw request respond."""
+ payload = json.dumps(data) if data and "files" not in kwargs else data
+ headers = (
+ self._get_headers() | extra_headers
+ if extra_headers
+ else self._get_headers()
+ )
+
+ return requests.delete(self.url(), data=payload, headers=headers,
**kwargs)
+
+ def delete(self, data=None, extra_headers: dict = None, **kwargs):
+ """Call Delete and process respond. Return True if ok"""
+ resp = self.raw_delete(data, extra_headers, **kwargs)
if 200 <= resp.status_code <= 299:
if resp.status_code == 204:
return True
@@ -204,63 +275,71 @@
def _get_resource(self, **kwargs):
return self.__class__(**kwargs)
-class Api(object):
+
+class Api:
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
- def login(self, password, email):
- assert('LOGIN' in self.options)
- data = {'email': email, 'password': password}
- url = '{0}/{1}'.format(self.base_url, self.options['LOGIN'])
+ def login(self, password, username=None):
+ assert "LOGIN" in self.options
+ # This allows us to suport both a {'email': username} and {'username":
username}
+ # Default to 'username' which is the default DRF behavior
+ username_key = self.options.get("USERNAME_KEY", "username")
+ data = {"password": password}
+ data[username_key] = username
+ url = "{0}/{1}".format(self.base_url, self.options["LOGIN"])
payload = json.dumps(data)
r = requests.post(url, data=payload, headers=DEFAULT_HEADERS)
- if r.status_code == 200:
+ if r.status_code in [200, 201]:
content = json.loads(r.content.decode())
- if self.token_type in content:
- self.token = content[self.token_type]
-
- self.username = content['username']
- logger.info('Welcome @{0} (token: {1})'.format(self.username,
self.token))
+ 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")
+ self.username = username
return True
else:
- logger.error('Login failed: ' + str(r.status_code) + ' ' +
r.content.decode())
+ logger.error(
+ "Login failed: " + str(r.status_code) + " " +
r.content.decode()
+ )
return False
def logout(self):
- assert('LOGOUT' in self.options)
- url = '{0}/{1}'.format(self.base_url, self.options['LOGOUT'])
+ assert "LOGOUT" in self.options
+ 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:
- logger.error('Logout failed: ' + str(r.status_code) + ' ' +
r.content.decode())
+ logger.error(
+ "Logout failed: " + str(r.status_code) + " " +
r.content.decode()
+ )
def __getattr__(self, item):
"""
@@ -273,15 +352,17 @@
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,
- 'use_token': self.use_token,
- 'token_format': self.token_format,
+ "token": self.token,
+ "base_url": f"{self.base_url}/{item}/",
+ "use_token": self.use_token,
+ "options": self.options,
}
- kwargs.update({'base_url': '{0}/{1}/'.format(kwargs['base_url'],
item)})
return self._get_resource(**kwargs)
def _get_resource(self, **kwargs):
- return self.resource_class(**kwargs)
\ No newline at end of file
+ return self.resource_class(**kwargs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-rest-framework-client-0.1.1/drf_client/exceptions.py
new/django-rest-framework-client-0.10.0/drf_client/exceptions.py
--- old/django-rest-framework-client-0.1.1/drf_client/exceptions.py
2017-05-07 21:11:26.000000000 +0200
+++ new/django-rest-framework-client-0.10.0/drf_client/exceptions.py
2023-10-05 03:44:46.000000000 +0200
@@ -1,5 +1,3 @@
-
-
class RestBaseException(Exception):
"""
All Rest exceptions inherit from this exception.
@@ -58,4 +56,4 @@
class ImproperlyConfigured(RestBaseException):
"""
Rest is somehow improperly configured.
- """
\ No newline at end of file
+ """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-rest-framework-client-0.1.1/drf_client/helpers/base_facade.py
new/django-rest-framework-client-0.10.0/drf_client/helpers/base_facade.py
--- old/django-rest-framework-client-0.1.1/drf_client/helpers/base_facade.py
1970-01-01 01:00:00.000000000 +0100
+++ new/django-rest-framework-client-0.10.0/drf_client/helpers/base_facade.py
2023-10-05 03:44:46.000000000 +0200
@@ -0,0 +1,25 @@
+"""Hold static information that can be accessed by any part of the package.
+
+A facade is an object that serves as a front-facing interface masking more
complex
+underlying or structural code.
+"""
+from argparse import Namespace
+
+from drf_client.connection import Api as RestApi
+
+
+class BaseFacade:
+ """Stores key static information used across the package."""
+
+ api: RestApi or None = None
+ api_options: dict or None = None
+ cmd_args: Namespace or None = None
+
+ @staticmethod
+ def initialize_api(api_options: dict, cmd_args: Namespace = None):
+ """Initialize API with the given options."""
+ if BaseFacade.api is None:
+ # Only initialize ones
+ BaseFacade.api_options = api_options.copy()
+ BaseFacade.api = RestApi(api_options)
+ BaseFacade.cmd_args = cmd_args
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-rest-framework-client-0.1.1/drf_client/helpers/base_main.py
new/django-rest-framework-client-0.10.0/drf_client/helpers/base_main.py
--- old/django-rest-framework-client-0.1.1/drf_client/helpers/base_main.py
1970-01-01 01:00:00.000000000 +0100
+++ new/django-rest-framework-client-0.10.0/drf_client/helpers/base_main.py
2023-10-05 03:44:46.000000000 +0200
@@ -0,0 +1,166 @@
+import argparse
+import getpass
+import logging
+import os
+import sys
+from urllib.parse import urlparse
+
+from .base_facade import BaseFacade
+
+LOG = logging.getLogger(__name__)
+
+
+class BaseMain:
+ """Boiler plate code for basic scripts.
+
+ The class assumes that most scripts include the basic following flow:
+
+ - Parse arguments
+ - Setup LOG configuration
+ - Login
+ - Do something after logging in
+"""
+
+ parser = None
+ args = None
+ api = None
+ options = {
+ "DOMAIN": None,
+ "API_PREFIX": "api/v1",
+ "TOKEN_TYPE": "jwt",
+ "TOKEN_FORMAT": "JWT {token}",
+ "USERNAME_KEY": "username",
+ "LOGIN": "auth/login/",
+ "LOGOUT": "auth/logout/",
+ "USE_DASHES": False,
+ }
+ logging_level = logging.INFO
+
+ def __init__(self):
+ """
+ Initialize Logging configuration
+ Initialize argument parsing
+ Process any extra arguments
+ Only hard codes one required argument: --user
+ Additional arguments can be configured by overwriting the
add_extra_args() method
+ Logging configuration can be changed by overwritting the
config_logging() method
+ """
+ self.parser = argparse.ArgumentParser(description=__doc__)
+ self.parser.add_argument(
+ "-u",
+ "--user",
+ dest="username",
+ type=str,
+ required=False,
+ help="Username used for login",
+ )
+ self.parser.add_argument(
+ "-t",
+ "--use-token",
+ dest="use_token",
+ action="store_true",
+ help="Use token (expects DRF_CLIENT_AUTH_TOKEN to be defined as an
env variable)",
+ )
+ self.parser.add_argument(
+ "--server",
+ dest="server",
+ type=str,
+ required=True,
+ help="Server Domain Name to use",
+ )
+
+ self.add_extra_args()
+
+ self.args = self.parser.parse_args()
+ self.config_logging()
+ self.domain = ""
+
+ def _critical_exit(self, msg):
+ """Exit with an error."""
+ LOG.error(msg)
+ sys.exit(1)
+
+ def main(self):
+ """
+ Main function to call to initiate execution.
+ 1. Get domain name and use to instantiate Api object
+ 2. Call before_login to allow for work before logging in
+ 3. Logging into the server
+ 4. Call after_loging to do actual work with server data
+ """
+ self.domain = self.get_domain()
+ # Create a static pointer to the API for global access
+ BaseFacade.initialize_api(api_options=self.get_options(),
cmd_args=self.args)
+ self.api = BaseFacade.api
+ self.before_login()
+ ok = self.login()
+ if ok:
+ self.after_login()
+
+ # Following functions can be overwritten if needed
+ # ================================================
+
+ def get_options(self):
+ """Add domain to Api options."""
+ options = self.options
+ options["DOMAIN"] = self.domain
+ return options
+
+ def config_logging(self):
+ """
+ Overwrite to change the way the logging package is configured
+ :return: Nothing
+ """
+ logging.basicConfig(
+ level=self.logging_level,
+ format="[%(asctime)-15s] %(levelname)-6s %(message)s",
+ datefmt="%d/%b/%Y %H:%M:%S",
+ )
+
+ def add_extra_args(self):
+ """
+ Overwrite to change the way extra arguments are added to the args
parser
+ :return: Nothing
+ """
+ pass
+
+ def get_domain(self) -> str:
+ """
+ Figure out server domain URL based on --server and --customer args
+ """
+ if not urlparse(self.args.server).scheme:
+ return f"https://{self.args.server}"
+ return self.args.server
+
+ def login(self) -> bool:
+ """
+ Get password from user and login
+ """
+ if self.args.use_token:
+ token = os.getenv("DRF_CLIENT_AUTH_TOKEN")
+ if not token:
+ self._critical_exit("DRF_CLIENT_AUTH_TOKEN must be defined as
environment variable.")
+ self.api.set_token(token)
+ LOG.info("Bearer Token has been set.")
+ ok = True
+ else:
+ password = getpass.getpass()
+ ok = self.api.login(username=self.args.username, password=password)
+ if ok:
+ LOG.info("Welcome {0}.".format(self.args.username))
+ return ok
+
+ def before_login(self):
+ """
+ Overwrite to do work after parsing, but before logging in to the server
+ This is a good place to do additional custom argument checks
+ :return: Nothing
+ """
+ pass
+
+ def after_login(self):
+ """
+ This function MUST be overwritten to do actual work after logging into
the Server
+ :return: Nothing
+ """
+ LOG.warning("No actual work done")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-rest-framework-client-0.1.1/example.py
new/django-rest-framework-client-0.10.0/example.py
--- old/django-rest-framework-client-0.1.1/example.py 2017-05-07
21:11:26.000000000 +0200
+++ new/django-rest-framework-client-0.10.0/example.py 2023-10-05
03:44:46.000000000 +0200
@@ -2,50 +2,54 @@
import logging
import sys
from pprint import pprint
+
from drf_client.connection import Api as RestApi
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
logger = logging.getLogger(__name__)
-email = input('Email? ')
+username = input("Email? ")
password = getpass.getpass()
options = {
- 'DOMAIN': 'http://127.0.0.1:8000',
- 'API_PREFIX': 'api/v1',
- 'TOKEN_TYPE': 'jwt',
- 'TOKEN_FORMAT': 'JWT {token}',
- 'LOGIN': 'auth/login/',
- 'LOGOUT': 'auth/logout/',
+ "DOMAIN": "http://127.0.0.1:8000",
+ "API_PREFIX": "api/v1",
+ "TOKEN_TYPE": "jwt",
+ "TOKEN_FORMAT": "JWT {token}",
+ "USERNAME_KEY": "username",
+ "LOGIN": "auth/login/",
+ "LOGOUT": "auth/logout/",
+ "USE_DASHES": False,
}
c = RestApi(options)
-ok = c.login(email=email, password=password)
+ok = c.login(username=username, password=password)
if ok:
-
# GET some data
my_objects = c.org.get()
- for obj in my_objects['results']:
+ for obj in my_objects["results"]:
pprint(obj)
- logger.info('------------------------------')
+ logger.info("------------------------------")
- logger.info('------------------------------')
- logger.info('------------------------------')
- my_object = c.org('arch-internal').get()
+ logger.info("------------------------------")
+ logger.info("------------------------------")
+ # If the URL includes "-", add under parenthesis:
+ # GET: /api/v1/someresource/some-path/
+ my_object = c.someresource("some-path").get()
pprint(my_object)
- logger.info('------------------------------')
- logger.info('------------------------------')
+ logger.info("------------------------------")
+ logger.info("------------------------------")
payload = {
- 'data1': 'val1',
- 'data2': 'val2',
+ "data1": "val1",
+ "data2": "val2",
}
- resp = c.org.post(data=payload)
+ resp = c.someresource.post(data=payload)
pprint(resp)
- logger.info('------------------------------')
+ logger.info("------------------------------")
c.logout()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-rest-framework-client-0.1.1/requirements-build.txt
new/django-rest-framework-client-0.10.0/requirements-build.txt
--- old/django-rest-framework-client-0.1.1/requirements-build.txt
2017-05-07 21:11:26.000000000 +0200
+++ new/django-rest-framework-client-0.10.0/requirements-build.txt
2023-10-05 03:44:46.000000000 +0200
@@ -1,3 +1,7 @@
pip
-twine
-tox
\ No newline at end of file
+twine==3.2.0
+setuptools>=40.8.0
+wheel
+tox
+isort==5.12.0
+black==23.7.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-rest-framework-client-0.1.1/requirements-test.txt
new/django-rest-framework-client-0.10.0/requirements-test.txt
--- old/django-rest-framework-client-0.1.1/requirements-test.txt
2017-05-07 21:11:26.000000000 +0200
+++ new/django-rest-framework-client-0.10.0/requirements-test.txt
2023-10-05 03:44:46.000000000 +0200
@@ -1,5 +1,4 @@
pytest
-unittest2
coverage==3.7.1
coveralls
mock
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-rest-framework-client-0.1.1/setup.py
new/django-rest-framework-client-0.10.0/setup.py
--- old/django-rest-framework-client-0.1.1/setup.py 2017-05-07
21:11:26.000000000 +0200
+++ new/django-rest-framework-client-0.10.0/setup.py 2023-10-05
03:44:46.000000000 +0200
@@ -1,24 +1,48 @@
+import pathlib
+
from setuptools import setup
-setup(name='django-rest-framework-client',
- version='0.1.1',
- description='Python client for a DjangoRestFramework based web site',
- url='https://github.com/dkarchmer/django-rest-framework-client',
- author='David Karchmer',
+# The directory containing this file
+HERE = pathlib.Path(__file__).parent
+
+# The text of the README file
+README = (HERE / "README.md").read_text()
+
+setup(
+ name="django-rest-framework-client",
+ version="0.10.0",
+ description="Python client for a DjangoRestFramework based web site",
+ long_description=README,
+ long_description_content_type="text/markdown",
+ url="https://github.com/dkarchmer/django-rest-framework-client",
+ author="David Karchmer",
author_email="[email protected]",
- license='MIT',
+ license="MIT",
packages=[
- 'drf_client',
+ "drf_client",
+ "drf_client.helpers",
],
install_requires=[
- 'requests',
+ "requests",
+ ],
+ python_requires=">=3.10,<4",
+ keywords=[
+ "django",
+ "djangorestframework",
+ "drf",
+ "rest-client",
],
- keywords=["django", "djangorestframework", "Rest",],
classifiers=[
"Programming Language :: Python",
- "Development Status :: 3 - Alpha",
+ "Programming Language :: Python :: 3",
+ "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",
"Operating System :: OS Independent",
- "Topic :: Software Development :: Libraries :: Python Modules"
+ "Topic :: Software Development :: Libraries :: Python Modules",
],
- zip_safe=False)
\ No newline at end of file
+ zip_safe=False,
+)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-rest-framework-client-0.1.1/tests/__init__.py
new/django-rest-framework-client-0.10.0/tests/__init__.py
--- old/django-rest-framework-client-0.1.1/tests/__init__.py 2017-05-07
21:11:26.000000000 +0200
+++ new/django-rest-framework-client-0.10.0/tests/__init__.py 2023-10-05
03:44:46.000000000 +0200
@@ -5,11 +5,12 @@
def get_tests():
return full_suite()
+
def full_suite():
- from .resources import ResourceTestCase
from .api import ApiTestCase
+ from .resources import ResourceTestCase
resourcesuite =
unittest.TestLoader().loadTestsFromTestCase(ResourceTestCase)
apisuite = unittest.TestLoader().loadTestsFromTestCase(ApiTestCase)
- return unittest.TestSuite([resourcesuite, apisuite])
\ No newline at end of file
+ return unittest.TestSuite([resourcesuite, apisuite])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-rest-framework-client-0.1.1/tests/api.py
new/django-rest-framework-client-0.10.0/tests/api.py
--- old/django-rest-framework-client-0.1.1/tests/api.py 2017-05-07
21:11:26.000000000 +0200
+++ new/django-rest-framework-client-0.10.0/tests/api.py 2023-10-05
03:44:46.000000000 +0200
@@ -1,9 +1,10 @@
-import sys
import json
+import sys
+import unittest
+
import mock
import requests
import requests_mock
-import unittest2 as unittest
from drf_client.connection import Api
from drf_client.exceptions import HttpClientError, HttpServerError
@@ -14,12 +15,13 @@
def setUp(self):
options = {
- 'DOMAIN': 'https://example.com',
- 'API_PREFIX': 'api/v1',
- 'TOKEN_TYPE': 'jwt',
- 'TOKEN_FORMAT': 'JWT {token}',
- 'LOGIN': 'auth/login/',
- 'LOGOUT': 'auth/logout/',
+ "DOMAIN": "https://example.com",
+ "API_PREFIX": "api/v1",
+ "TOKEN_TYPE": "jwt",
+ "TOKEN_FORMAT": "JWT {token}",
+ "LOGIN": "auth/login/",
+ "LOGOUT": "auth/logout/",
+ "USE_DASHES": False,
}
self.api = Api(options=options)
@@ -28,161 +30,153 @@
self.api = None
def test_init(self):
-
- self.assertEqual(self.api.base_url, 'https://example.com/api/v1')
+ 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):
-
self.assertEqual(self.api.token, None)
- self.api.set_token('big-token')
- self.assertEqual(self.api.token, 'big-token')
+ self.api.set_token("big-token")
+ self.assertEqual(self.api.token, "big-token")
@requests_mock.Mocker()
def test_login(self, m):
- payload = {
- 'jwt': 'big-token',
- 'username': 'user1'
- }
- m.post('https://example.com/api/v1/auth/login/',
text=json.dumps(payload))
+ payload = {"jwt": "big-token", "username": "user1"}
+ m.post("https://example.com/api/v1/auth/login/",
text=json.dumps(payload))
- ok = self.api.login(email='[email protected]', password='pass')
+ ok = self.api.login(username="[email protected]", password="pass")
self.assertTrue(ok)
- self.assertEqual(self.api.username, 'user1')
- self.assertEqual(self.api.token, 'big-token')
+ self.assertEqual(self.api.username, "[email protected]")
+ self.assertEqual(self.api.token, "big-token")
@requests_mock.Mocker()
def test_logout(self, m):
- payload = {
- 'jwt': 'big-token',
- 'username': 'user1'
- }
- m.post('https://example.com/api/v1/auth/login/',
text=json.dumps(payload))
- m.post('https://example.com/api/v1/auth/logout/', status_code=204)
+ payload = {"jwt": "big-token", "username": "user1"}
+ m.post("https://example.com/api/v1/auth/login/",
text=json.dumps(payload))
+ m.post("https://example.com/api/v1/auth/logout/", status_code=204)
- ok = self.api.login(email='[email protected]', password='pass')
+ ok = self.api.login(username="[email protected]", password="pass")
self.assertTrue(ok)
self.api.logout()
self.assertEqual(self.api.username, None)
self.assertEqual(self.api.token, None)
-
@requests_mock.Mocker()
def test_get_list(self, m):
- payload = {
- "result": ["a", "b", "c"]
- }
- m.get('https://example.com/api/v1/test/', text=json.dumps(payload))
+ payload = {"result": ["a", "b", "c"]}
+ m.get("https://example.com/api/v1/test/", text=json.dumps(payload))
resp = self.api.test.get()
- self.assertEqual(resp['result'], ['a', 'b', 'c'])
+ self.assertEqual(resp["result"], ["a", "b", "c"])
@requests_mock.Mocker()
def test_get_detail(self, m):
- payload = {
- "a": "b",
- "c": "d"
- }
- m.get('https://example.com/api/v1/test/my-detail/',
text=json.dumps(payload))
+ payload = {"a": "b", "c": "d"}
+ m.get("https://example.com/api/v1/test/my-detail/",
text=json.dumps(payload))
- resp = self.api.test('my-detail').get()
- self.assertEqual(resp, {'a': 'b', 'c': 'd'})
+ resp = self.api.test("my-detail").get()
+ self.assertEqual(resp, {"a": "b", "c": "d"})
@requests_mock.Mocker()
def test_get_detail_with_action(self, m):
- payload = {
- "a": "b",
- "c": "d"
- }
- 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.get()
- self.assertEqual(resp, {'a': 'b', 'c': 'd'})
+ payload = {"a": "b", "c": "d"}
+ 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.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 = {
- "a": "b",
- "c": "d"
- }
- m.get('https://example.com/api/v1/test/my-detail/',
text=json.dumps(payload))
+ payload = {"a": "b", "c": "d"}
+ m.get("https://example.com/api/v1/test/my-detail/",
text=json.dumps(payload))
- resp = self.api.test('my-detail').get(foo='bar')
- self.assertEqual(resp, {'a': 'b', 'c': 'd'})
+ resp = self.api.test("my-detail").get(foo="bar")
+ self.assertEqual(resp, {"a": "b", "c": "d"})
@requests_mock.Mocker()
def test_post(self, m):
- payload = {
- "foo": ["a", "b", "c"]
- }
- result = {
- "id": 1
- }
- m.post('https://example.com/api/v1/test/', text=json.dumps(result))
+ payload = {"foo": ["a", "b", "c"]}
+ result = {"id": 1}
+ m.post("https://example.com/api/v1/test/", text=json.dumps(result))
resp = self.api.test.post(payload)
- self.assertEqual(resp['id'], 1)
+ self.assertEqual(resp["id"], 1)
@requests_mock.Mocker()
def test_patch(self, m):
- payload = {
- "foo": ["a", "b", "c"]
- }
- result = {
- "id": 1
- }
- m.patch('https://example.com/api/v1/test/my-detail/',
text=json.dumps(result))
+ payload = {"foo": ["a", "b", "c"]}
+ result = {"id": 1}
+ m.patch("https://example.com/api/v1/test/my-detail/",
text=json.dumps(result))
- resp = self.api.test('my-detail').patch(payload)
- self.assertEqual(resp['id'], 1)
+ resp = self.api.test("my-detail").patch(payload)
+ self.assertEqual(resp["id"], 1)
@requests_mock.Mocker()
def test_put(self, m):
- payload = {
- "foo": ["a", "b", "c"]
- }
- result = {
- "id": 1
- }
- m.put('https://example.com/api/v1/test/my-detail/',
text=json.dumps(result))
+ payload = {"foo": ["a", "b", "c"]}
+ result = {"id": 1}
+ m.put("https://example.com/api/v1/test/my-detail/",
text=json.dumps(result))
- resp = self.api.test('my-detail').put(payload)
- self.assertEqual(resp['id'], 1)
+ resp = self.api.test("my-detail").put(payload)
+ self.assertEqual(resp["id"], 1)
@requests_mock.Mocker()
def test_delete(self, m):
- result = {
- "id": 1
- }
- m.delete('https://example.com/api/v1/test/my-detail/',
text=json.dumps(result))
+ result = {"id": 1}
+ m.delete("https://example.com/api/v1/test/my-detail/",
text=json.dumps(result))
- deleted = self.api.test('my-detail').delete()
+ deleted = self.api.test("my-detail").delete()
+ self.assertTrue(deleted)
+
+ result = {"id": 2}
+ m.delete("https://example.com/api/v1/test/my-detail2/",
text=json.dumps(result))
+
+ deleted = self.api.test("my-detail2").delete(data={"foo": "bar"})
self.assertTrue(deleted)
@requests_mock.Mocker()
def test_post_with_error(self, m):
- payload = {
- "foo": ["a", "b", "c"]
- }
- result = {
- "id": 1
- }
- m.post('https://example.com/api/v1/test/', status_code=400,
text=json.dumps(result))
+ payload = {"foo": ["a", "b", "c"]}
+ result = {"id": 1}
+ m.post(
+ "https://example.com/api/v1/test/", status_code=400,
text=json.dumps(result)
+ )
with self.assertRaises(HttpClientError):
self.api.test.post(payload)
- m.post('https://example.com/api/v1/test/', status_code=404,
text=json.dumps(result))
+ m.post(
+ "https://example.com/api/v1/test/", status_code=404,
text=json.dumps(result)
+ )
with self.assertRaises(HttpClientError):
self.api.test.post(payload)
- m.post('https://example.com/api/v1/test/', status_code=500,
text=json.dumps(result))
+ m.post(
+ "https://example.com/api/v1/test/", status_code=500,
text=json.dumps(result)
+ )
with self.assertRaises(HttpServerError):
self.api.test.post(payload)
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-rest-framework-client-0.1.1/tests/helpers.py
new/django-rest-framework-client-0.10.0/tests/helpers.py
--- old/django-rest-framework-client-0.1.1/tests/helpers.py 1970-01-01
01:00:00.000000000 +0100
+++ new/django-rest-framework-client-0.10.0/tests/helpers.py 2023-10-05
03:44:46.000000000 +0200
@@ -0,0 +1,14 @@
+"""test Resource class."""
+import unittest
+
+from drf_client.helpers.base_facade import BaseFacade
+
+
+class FacadeTestCase(unittest.TestCase):
+ """Test static facade class."""
+
+ def test_initialize_facade(self):
+ """Test Initializer."""
+ BaseFacade.initialize_api(api_options={"DOMAIN":
"https://example.com"})
+ assert BaseFacade.api_options["DOMAIN"] == "https://example.com"
+ assert BaseFacade.api is not None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-rest-framework-client-0.1.1/tests/resources.py
new/django-rest-framework-client-0.10.0/tests/resources.py
--- old/django-rest-framework-client-0.1.1/tests/resources.py 2017-05-07
21:11:26.000000000 +0200
+++ new/django-rest-framework-client-0.10.0/tests/resources.py 2023-10-05
03:44:46.000000000 +0200
@@ -1,44 +1,48 @@
-import sys
+"""test Resource class."""
import json
-import mock
-import requests
+import unittest
+
import requests_mock
-import unittest2 as unittest
-from drf_client.connection import Api, RestResource
-from drf_client import exceptions
+from drf_client.connection import RestResource
class ResourceTestCase(unittest.TestCase):
-
def setUp(self):
- self.base_resource =
RestResource(base_url="https://example.com/api/v1/test/",
- use_token=True,
- token_format='JWT {token}',
- token_type='jwt',
- token='my-token')
+ 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,
+ options=self.options,
+ token="my-token",
+ )
def test_url(self):
-
url = self.base_resource.url()
- self.assertEqual(url, 'https://example.com/api/v1/test/')
+ self.assertEqual(url, "https://example.com/api/v1/test/")
def test_headers(self):
expected_headers = {
- 'Content-Type': 'application/json',
- 'Authorization': 'JWT my-token'
+ "Content-Type": "application/json",
+ "Authorization": "JWT my-token",
}
- headers = self.base_resource._get_header()
+ headers = self.base_resource._get_headers()
self.assertEqual(headers, expected_headers)
@requests_mock.Mocker()
def test_get_200(self, m):
- payload = {
- "result": ["a", "b", "c"]
- }
- m.get('https://example.com/api/v1/test/', text=json.dumps(payload))
+ payload = {"result": ["a", "b", "c"]}
+ m.get("https://example.com/api/v1/test/", text=json.dumps(payload))
resp = self.base_resource.get()
- self.assertEqual(resp['result'], ['a', 'b', 'c'])
-
+ self.assertEqual(resp["result"], ["a", "b", "c"])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-rest-framework-client-0.1.1/tox.ini
new/django-rest-framework-client-0.10.0/tox.ini
--- old/django-rest-framework-client-0.1.1/tox.ini 2017-05-07
21:11:26.000000000 +0200
+++ new/django-rest-framework-client-0.10.0/tox.ini 2023-10-05
03:44:46.000000000 +0200
@@ -4,7 +4,7 @@
# and then run "tox" from this directory.
[tox]
-envlist = py27, py34, py36
+envlist = py310, py311
[testenv]
deps =
@@ -13,4 +13,4 @@
commands =
coverage run --source=drf_client -m py.test
coverage report -m
-usedevelop = true
\ No newline at end of file
+usedevelop = true
++++++ python-django-rest-framework-client-no-mock.patch ++++++
--- /var/tmp/diff_new_pack.Qi76kk/_old 2024-02-23 16:46:58.018899430 +0100
+++ /var/tmp/diff_new_pack.Qi76kk/_new 2024-02-23 16:46:58.022899575 +0100
@@ -1,23 +1,14 @@
-diff -upr django-rest-framework-client-0.1.1.orig/tests/api.py
django-rest-framework-client-0.1.1/tests/api.py
---- django-rest-framework-client-0.1.1.orig/tests/api.py 2022-05-12
09:32:27.295052321 +0200
-+++ django-rest-framework-client-0.1.1/tests/api.py 2022-05-12
09:32:27.299052347 +0200
-@@ -1,6 +1,6 @@
+Index: django-rest-framework-client-0.10.0/tests/api.py
+===================================================================
+--- django-rest-framework-client-0.10.0.orig/tests/api.py
++++ django-rest-framework-client-0.10.0/tests/api.py
+@@ -2,7 +2,7 @@ import json
import sys
- import json
--import mock
-+from unittest import mock
- import requests
- import requests_mock
import unittest
-diff -upr django-rest-framework-client-0.1.1.orig/tests/resources.py
django-rest-framework-client-0.1.1/tests/resources.py
---- django-rest-framework-client-0.1.1.orig/tests/resources.py 2022-05-12
09:32:27.295052321 +0200
-+++ django-rest-framework-client-0.1.1/tests/resources.py 2022-05-12
09:32:27.299052347 +0200
-@@ -1,6 +1,6 @@
- import sys
- import json
+
-import mock
+from unittest import mock
import requests
import requests_mock
- import unittest
+