2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-29 03:56:43 +00:00
* Add basic endpoint for group information

* Add hardcoded api-url lookup function for django models

* Adds JS function for rendering a 'group'

* Fix typo

* Add unit tests for new endpoints

* Increment API version

* JS linting
This commit is contained in:
Oliver 2023-02-11 07:09:59 +11:00 committed by GitHub
parent 06605e70c5
commit f4e8c05165
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 147 additions and 8 deletions

View File

@ -2,11 +2,14 @@
# InvenTree API version # 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 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 v93 -> 2023-02-03 : https://github.com/inventree/InvenTree/pull/4300
- Adds extra information to the currency exchange endpoint - Adds extra information to the currency exchange endpoint
- Adds API endpoint for manually updating exchange rates - Adds API endpoint for manually updating exchange rates

View File

@ -704,6 +704,17 @@ class BaseInvenTreeSetting(models.Model):
except Exception: except Exception:
pass 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 return None
def is_bool(self): def is_bool(self):

View File

@ -11,6 +11,7 @@
modalShowSubmitButton, modalShowSubmitButton,
renderBuild, renderBuild,
renderCompany, renderCompany,
renderGroup,
renderManufacturerPart, renderManufacturerPart,
renderOwner, renderOwner,
renderPart, renderPart,
@ -2073,6 +2074,9 @@ function renderModelData(name, model, data, parameters, options) {
case 'user': case 'user':
renderer = renderUser; renderer = renderUser;
break; break;
case 'group':
renderer = renderGroup;
break;
default: default:
break; break;
} }

View File

@ -8,6 +8,7 @@
/* exported /* exported
renderBuild, renderBuild,
renderCompany, renderCompany,
renderGroup,
renderManufacturerPart, renderManufacturerPart,
renderOwner, renderOwner,
renderPart, renderPart,
@ -15,6 +16,7 @@
renderStockItem, renderStockItem,
renderStockLocation, renderStockLocation,
renderSupplierPart, renderSupplierPart,
renderUser,
*/ */
@ -216,6 +218,17 @@ function renderPart(name, data, parameters={}, options={}) {
return html; return html;
} }
// Renderer for "Group" model
// eslint-disable-next-line no-unused-vars
function renderGroup(name, data, parameters={}, options={}) {
var html = `<span>${data.name}</span>`;
return html;
}
// Renderer for "User" model // Renderer for "User" model
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
function renderUser(name, data, parameters={}, options={}) { function renderUser(name, data, parameters={}, options={}) {

View File

@ -1,6 +1,6 @@
"""DRF API definition for the 'users' app""" """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.core.exceptions import ObjectDoesNotExist
from django.urls import include, path, re_path 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.mixins import ListAPI, RetrieveAPI, RetrieveUpdateAPI
from InvenTree.serializers import UserSerializer from InvenTree.serializers import UserSerializer
from users.models import Owner, RuleSet, check_user_role from users.models import Owner, RuleSet, check_user_role
from users.serializers import OwnerSerializer from users.serializers import GroupSerializer, OwnerSerializer
class OwnerList(ListAPI): class OwnerList(ListAPI):
@ -113,7 +113,9 @@ class UserDetail(RetrieveAPI):
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = UserSerializer serializer_class = UserSerializer
permission_classes = (permissions.IsAuthenticated,) permission_classes = [
permissions.IsAuthenticated
]
class MeUserDetail(RetrieveUpdateAPI, UserDetail): class MeUserDetail(RetrieveUpdateAPI, UserDetail):
@ -129,7 +131,9 @@ class UserList(ListAPI):
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = UserSerializer serializer_class = UserSerializer
permission_classes = (permissions.IsAuthenticated,) permission_classes = [
permissions.IsAuthenticated,
]
filter_backends = [ filter_backends = [
DjangoFilterBackend, 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): class GetAuthToken(APIView):
"""Return authentication token for an authenticated user.""" """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'^.*$', OwnerList.as_view(), name='api-owner-list'),
])), ])),
re_path(r'^(?P<pk>[0-9]+)/?$', UserDetail.as_view(), name='user-detail'), re_path(r'^group/', include([
path('', UserList.as_view()), re_path(r'^(?P<pk>[0-9]+)/?$', GroupDetail.as_view(), name='api-group-detail'),
re_path(r'^.*$', GroupList.as_view(), name='api-group-list'),
])),
re_path(r'^(?P<pk>[0-9]+)/?$', UserDetail.as_view(), name='api-user-detail'),
path('', UserList.as_view(), name='api-user-list'),
] ]

View File

@ -1,5 +1,6 @@
"""DRF API serializers for the 'users' app""" """DRF API serializers for the 'users' app"""
from django.contrib.auth.models import Group
from rest_framework import serializers from rest_framework import serializers
@ -24,3 +25,16 @@ class OwnerSerializer(InvenTreeModelSerializer):
'name', 'name',
'label', 'label',
] ]
class GroupSerializer(InvenTreeModelSerializer):
"""Serializer for a 'Group'"""
class Meta:
"""Metaclass defines serializer fields"""
model = Group
fields = [
'pk',
'name',
]

View File

@ -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)

View File

@ -231,7 +231,7 @@ class OwnerModelTest(InvenTreeTestCase):
# self.assertEqual(response['owner_id'], group.pk) # self.assertEqual(response['owner_id'], group.pk)
# own user detail # 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) self.assertEqual(response_detail['username'], self.username)
response_me = self.do_request(reverse('api-user-me'), {}, 200) response_me = self.do_request(reverse('api-user-me'), {}, 200)