diff --git a/InvenTree/InvenTree/api_version.py b/InvenTree/InvenTree/api_version.py
index 2a8042f4f7..7f207ec394 100644
--- a/InvenTree/InvenTree/api_version.py
+++ b/InvenTree/InvenTree/api_version.py
@@ -2,11 +2,14 @@
# InvenTree API version
-INVENTREE_API_VERSION = 93
+INVENTREE_API_VERSION = 94
"""
Increment this API version number whenever there is a significant change to the API that any clients need to know about
+v94 -> 2023-02-10 : https://github.com/inventree/InvenTree/pull/4327
+ - Adds API endpoints for the "Group" auth model
+
v93 -> 2023-02-03 : https://github.com/inventree/InvenTree/pull/4300
- Adds extra information to the currency exchange endpoint
- Adds API endpoint for manually updating exchange rates
diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py
index f42b9ee5c5..d71d9e3aba 100644
--- a/InvenTree/common/models.py
+++ b/InvenTree/common/models.py
@@ -704,6 +704,17 @@ class BaseInvenTreeSetting(models.Model):
except Exception:
pass
+ # Some other model types are hard-coded
+ hardcoded_models = {
+ 'auth.user': 'api-user-list',
+ 'auth.group': 'api-group-list',
+ }
+
+ model_table = f'{model_class._meta.app_label}.{model_class._meta.model_name}'
+
+ if url := hardcoded_models[model_table]:
+ return reverse(url)
+
return None
def is_bool(self):
diff --git a/InvenTree/templates/js/translated/forms.js b/InvenTree/templates/js/translated/forms.js
index 705eeb7f75..c7fc9452d1 100644
--- a/InvenTree/templates/js/translated/forms.js
+++ b/InvenTree/templates/js/translated/forms.js
@@ -11,6 +11,7 @@
modalShowSubmitButton,
renderBuild,
renderCompany,
+ renderGroup,
renderManufacturerPart,
renderOwner,
renderPart,
@@ -2073,6 +2074,9 @@ function renderModelData(name, model, data, parameters, options) {
case 'user':
renderer = renderUser;
break;
+ case 'group':
+ renderer = renderGroup;
+ break;
default:
break;
}
diff --git a/InvenTree/templates/js/translated/model_renderers.js b/InvenTree/templates/js/translated/model_renderers.js
index 97970f67d4..4c4e70122f 100644
--- a/InvenTree/templates/js/translated/model_renderers.js
+++ b/InvenTree/templates/js/translated/model_renderers.js
@@ -8,6 +8,7 @@
/* exported
renderBuild,
renderCompany,
+ renderGroup,
renderManufacturerPart,
renderOwner,
renderPart,
@@ -15,6 +16,7 @@
renderStockItem,
renderStockLocation,
renderSupplierPart,
+ renderUser,
*/
@@ -216,6 +218,17 @@ function renderPart(name, data, parameters={}, options={}) {
return html;
}
+
+// Renderer for "Group" model
+// eslint-disable-next-line no-unused-vars
+function renderGroup(name, data, parameters={}, options={}) {
+
+ var html = `${data.name}`;
+
+ return html;
+
+}
+
// Renderer for "User" model
// eslint-disable-next-line no-unused-vars
function renderUser(name, data, parameters={}, options={}) {
diff --git a/InvenTree/users/api.py b/InvenTree/users/api.py
index 1d3c942cef..7aa9a8cc02 100644
--- a/InvenTree/users/api.py
+++ b/InvenTree/users/api.py
@@ -1,6 +1,6 @@
"""DRF API definition for the 'users' app"""
-from django.contrib.auth.models import User
+from django.contrib.auth.models import Group, User
from django.core.exceptions import ObjectDoesNotExist
from django.urls import include, path, re_path
@@ -13,7 +13,7 @@ from rest_framework.views import APIView
from InvenTree.mixins import ListAPI, RetrieveAPI, RetrieveUpdateAPI
from InvenTree.serializers import UserSerializer
from users.models import Owner, RuleSet, check_user_role
-from users.serializers import OwnerSerializer
+from users.serializers import GroupSerializer, OwnerSerializer
class OwnerList(ListAPI):
@@ -113,7 +113,9 @@ class UserDetail(RetrieveAPI):
queryset = User.objects.all()
serializer_class = UserSerializer
- permission_classes = (permissions.IsAuthenticated,)
+ permission_classes = [
+ permissions.IsAuthenticated
+ ]
class MeUserDetail(RetrieveUpdateAPI, UserDetail):
@@ -129,7 +131,9 @@ class UserList(ListAPI):
queryset = User.objects.all()
serializer_class = UserSerializer
- permission_classes = (permissions.IsAuthenticated,)
+ permission_classes = [
+ permissions.IsAuthenticated,
+ ]
filter_backends = [
DjangoFilterBackend,
@@ -143,6 +147,35 @@ class UserList(ListAPI):
]
+class GroupDetail(RetrieveAPI):
+ """Detail endpoint for a particular auth group"""
+
+ queryset = Group.objects.all()
+ serializer_class = GroupSerializer
+ permission_classes = [
+ permissions.IsAuthenticated,
+ ]
+
+
+class GroupList(ListAPI):
+ """List endpoint for all auth groups"""
+
+ queryset = Group.objects.all()
+ serializer_class = GroupSerializer
+ permission_classes = [
+ permissions.IsAuthenticated,
+ ]
+
+ filter_backends = [
+ DjangoFilterBackend,
+ filters.SearchFilter,
+ ]
+
+ search_fields = [
+ 'name',
+ ]
+
+
class GetAuthToken(APIView):
"""Return authentication token for an authenticated user."""
@@ -185,6 +218,12 @@ user_urls = [
re_path(r'^.*$', OwnerList.as_view(), name='api-owner-list'),
])),
- re_path(r'^(?P[0-9]+)/?$', UserDetail.as_view(), name='user-detail'),
- path('', UserList.as_view()),
+ re_path(r'^group/', include([
+ re_path(r'^(?P[0-9]+)/?$', GroupDetail.as_view(), name='api-group-detail'),
+ re_path(r'^.*$', GroupList.as_view(), name='api-group-list'),
+ ])),
+
+ re_path(r'^(?P[0-9]+)/?$', UserDetail.as_view(), name='api-user-detail'),
+
+ path('', UserList.as_view(), name='api-user-list'),
]
diff --git a/InvenTree/users/serializers.py b/InvenTree/users/serializers.py
index bde5cb87db..153fd02d8e 100644
--- a/InvenTree/users/serializers.py
+++ b/InvenTree/users/serializers.py
@@ -1,5 +1,6 @@
"""DRF API serializers for the 'users' app"""
+from django.contrib.auth.models import Group
from rest_framework import serializers
@@ -24,3 +25,16 @@ class OwnerSerializer(InvenTreeModelSerializer):
'name',
'label',
]
+
+
+class GroupSerializer(InvenTreeModelSerializer):
+ """Serializer for a 'Group'"""
+
+ class Meta:
+ """Metaclass defines serializer fields"""
+
+ model = Group
+ fields = [
+ 'pk',
+ 'name',
+ ]
diff --git a/InvenTree/users/test_api.py b/InvenTree/users/test_api.py
new file mode 100644
index 0000000000..1f4d466ba4
--- /dev/null
+++ b/InvenTree/users/test_api.py
@@ -0,0 +1,55 @@
+"""API tests for various user / auth API endpoints"""
+
+from django.contrib.auth.models import Group, User
+from django.urls import reverse
+
+from InvenTree.api_tester import InvenTreeAPITestCase
+
+
+class UserAPITests(InvenTreeAPITestCase):
+ """Tests for user API endpoints"""
+
+ def test_user_api(self):
+ """Tests for User API endpoints"""
+
+ response = self.get(
+ reverse('api-user-list'),
+ expected_code=200
+ )
+
+ # Check the correct number of results was returned
+ self.assertEqual(len(response.data), User.objects.count())
+
+ for key in ['username', 'pk', 'email']:
+ self.assertIn(key, response.data[0])
+
+ # Check detail URL
+ pk = response.data[0]['pk']
+
+ response = self.get(
+ reverse('api-user-detail', kwargs={'pk': pk}),
+ expected_code=200
+ )
+
+ self.assertIn('pk', response.data)
+ self.assertIn('username', response.data)
+
+ def test_group_api(self):
+ """Tests for the Group API endpoints"""
+
+ response = self.get(
+ reverse('api-group-list'),
+ expected_code=200,
+ )
+
+ self.assertIn('name', response.data[0])
+
+ self.assertEqual(len(response.data), Group.objects.count())
+
+ # Check detail URL
+ response = self.get(
+ reverse('api-group-detail', kwargs={'pk': response.data[0]['pk']}),
+ expected_code=200,
+ )
+
+ self.assertIn('name', response.data)
diff --git a/InvenTree/users/tests.py b/InvenTree/users/tests.py
index 406b9e83d3..5243d111b9 100644
--- a/InvenTree/users/tests.py
+++ b/InvenTree/users/tests.py
@@ -231,7 +231,7 @@ class OwnerModelTest(InvenTreeTestCase):
# self.assertEqual(response['owner_id'], group.pk)
# own user detail
- response_detail = self.do_request(reverse('user-detail', kwargs={'pk': self.user.id}), {}, 200)
+ response_detail = self.do_request(reverse('api-user-detail', kwargs={'pk': self.user.id}), {}, 200)
self.assertEqual(response_detail['username'], self.username)
response_me = self.do_request(reverse('api-user-me'), {}, 200)