mirror of
https://github.com/inventree/InvenTree.git
synced 2025-10-15 05:32:21 +00:00
Add schema representation for plugin action error responses (#10538)
* Define error serializer and codes for schema * Bump api version * Update integration test status code check
This commit is contained in:
@@ -1,12 +1,15 @@
|
|||||||
"""InvenTree API version information."""
|
"""InvenTree API version information."""
|
||||||
|
|
||||||
# InvenTree API version
|
# InvenTree API version
|
||||||
INVENTREE_API_VERSION = 406
|
INVENTREE_API_VERSION = 407
|
||||||
|
|
||||||
"""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."""
|
||||||
|
|
||||||
INVENTREE_API_TEXT = """
|
INVENTREE_API_TEXT = """
|
||||||
|
|
||||||
|
v407 -> 2025-10-09: https://github.com/inventree/InvenTree/pull/10538
|
||||||
|
- Breaking: Set error status code for plugin action call instead of just returning error data
|
||||||
|
|
||||||
v406 -> 2025-10-08: https://github.com/inventree/InvenTree/pull/10532
|
v406 -> 2025-10-08: https://github.com/inventree/InvenTree/pull/10532
|
||||||
- Set return type for background task overview
|
- Set return type for background task overview
|
||||||
- Implement serializer for part serial number detail
|
- Implement serializer for part serial number detail
|
||||||
|
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import serializers
|
from drf_spectacular.utils import OpenApiResponse, extend_schema
|
||||||
|
from rest_framework import serializers, status
|
||||||
from rest_framework.generics import GenericAPIView
|
from rest_framework.generics import GenericAPIView
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
@@ -18,12 +19,32 @@ class ActionPluginSerializer(serializers.Serializer):
|
|||||||
data = serializers.DictField()
|
data = serializers.DictField()
|
||||||
|
|
||||||
|
|
||||||
|
class ActionPluginErrorSerializer(serializers.Serializer):
|
||||||
|
"""Serializer for the ActionPluginView error responses."""
|
||||||
|
|
||||||
|
error = serializers.CharField()
|
||||||
|
action = serializers.CharField(required=False)
|
||||||
|
|
||||||
|
|
||||||
class ActionPluginView(GenericAPIView):
|
class ActionPluginView(GenericAPIView):
|
||||||
"""Endpoint for running custom action plugins."""
|
"""Endpoint for running custom action plugins."""
|
||||||
|
|
||||||
permission_classes = [InvenTree.permissions.IsAuthenticatedOrReadScope]
|
permission_classes = [InvenTree.permissions.IsAuthenticatedOrReadScope]
|
||||||
serializer_class = ActionPluginSerializer
|
serializer_class = ActionPluginSerializer
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
responses={
|
||||||
|
200: ActionPluginSerializer,
|
||||||
|
400: OpenApiResponse(
|
||||||
|
description='No action specified',
|
||||||
|
response=ActionPluginErrorSerializer(),
|
||||||
|
),
|
||||||
|
404: OpenApiResponse(
|
||||||
|
description='No matching action found',
|
||||||
|
response=ActionPluginErrorSerializer(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
"""This function checks if all required info was submitted and then performs a plugin_action or returns an error."""
|
"""This function checks if all required info was submitted and then performs a plugin_action or returns an error."""
|
||||||
action = request.data.get('action', None)
|
action = request.data.get('action', None)
|
||||||
@@ -31,7 +52,9 @@ class ActionPluginView(GenericAPIView):
|
|||||||
data = request.data.get('data', None)
|
data = request.data.get('data', None)
|
||||||
|
|
||||||
if action is None:
|
if action is None:
|
||||||
return Response({'error': _('No action specified')})
|
return Response(
|
||||||
|
{'error': _('No action specified')}, status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
action_plugins = registry.with_mixin(PluginMixinEnum.ACTION)
|
action_plugins = registry.with_mixin(PluginMixinEnum.ACTION)
|
||||||
for plugin in action_plugins:
|
for plugin in action_plugins:
|
||||||
@@ -43,4 +66,7 @@ class ActionPluginView(GenericAPIView):
|
|||||||
log_error('perform_action', plugin=plugin.slug)
|
log_error('perform_action', plugin=plugin.slug)
|
||||||
|
|
||||||
# If we got to here, no matching action was found
|
# If we got to here, no matching action was found
|
||||||
return Response({'error': _('No matching action found'), 'action': action})
|
return Response(
|
||||||
|
{'error': _('No matching action found'), 'action': action},
|
||||||
|
status=status.HTTP_404_NOT_FOUND,
|
||||||
|
)
|
||||||
|
@@ -83,12 +83,12 @@ class APITests(InvenTreeTestCase):
|
|||||||
"""Check the possible errors with post."""
|
"""Check the possible errors with post."""
|
||||||
# Test empty request
|
# Test empty request
|
||||||
response = self.client.post('/api/action/')
|
response = self.client.post('/api/action/')
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 400)
|
||||||
self.assertEqual(response.data, {'error': 'No action specified'})
|
self.assertEqual(response.data, {'error': 'No action specified'})
|
||||||
|
|
||||||
# Test non-existing action
|
# Test non-existing action
|
||||||
response = self.client.post('/api/action/', data={'action': 'nonexisting'})
|
response = self.client.post('/api/action/', data={'action': 'nonexisting'})
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 404)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
response.data,
|
response.data,
|
||||||
{'error': 'No matching action found', 'action': 'nonexisting'},
|
{'error': 'No matching action found', 'action': 'nonexisting'},
|
||||||
|
@@ -27,7 +27,7 @@ class SimpleActionPluginTests(InvenTreeTestCase):
|
|||||||
self.set_plugin_state(False)
|
self.set_plugin_state(False)
|
||||||
|
|
||||||
response = self.client.post('/api/action/', data=data)
|
response = self.client.post('/api/action/', data=data)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 404)
|
||||||
self.assertIn('error', response.data)
|
self.assertIn('error', response.data)
|
||||||
|
|
||||||
# Now enable the plugin
|
# Now enable the plugin
|
||||||
|
Reference in New Issue
Block a user