diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index dfaa761279..9d9cf990ca 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,12 +1,15 @@ """InvenTree API version information.""" # 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.""" 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 - Set return type for background task overview - Implement serializer for part serial number detail diff --git a/src/backend/InvenTree/plugin/base/action/api.py b/src/backend/InvenTree/plugin/base/action/api.py index 1cfdb57964..9012a2f06f 100644 --- a/src/backend/InvenTree/plugin/base/action/api.py +++ b/src/backend/InvenTree/plugin/base/action/api.py @@ -2,7 +2,8 @@ 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.response import Response @@ -18,12 +19,32 @@ class ActionPluginSerializer(serializers.Serializer): data = serializers.DictField() +class ActionPluginErrorSerializer(serializers.Serializer): + """Serializer for the ActionPluginView error responses.""" + + error = serializers.CharField() + action = serializers.CharField(required=False) + + class ActionPluginView(GenericAPIView): """Endpoint for running custom action plugins.""" permission_classes = [InvenTree.permissions.IsAuthenticatedOrReadScope] 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): """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) @@ -31,7 +52,9 @@ class ActionPluginView(GenericAPIView): data = request.data.get('data', 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) for plugin in action_plugins: @@ -43,4 +66,7 @@ class ActionPluginView(GenericAPIView): log_error('perform_action', plugin=plugin.slug) # 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, + ) diff --git a/src/backend/InvenTree/plugin/base/action/test_action.py b/src/backend/InvenTree/plugin/base/action/test_action.py index b59b82548e..a62121a73c 100644 --- a/src/backend/InvenTree/plugin/base/action/test_action.py +++ b/src/backend/InvenTree/plugin/base/action/test_action.py @@ -83,12 +83,12 @@ class APITests(InvenTreeTestCase): """Check the possible errors with post.""" # Test empty request 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'}) # Test non-existing action response = self.client.post('/api/action/', data={'action': 'nonexisting'}) - self.assertEqual(response.status_code, 200) + self.assertEqual(response.status_code, 404) self.assertEqual( response.data, {'error': 'No matching action found', 'action': 'nonexisting'}, diff --git a/src/backend/InvenTree/plugin/samples/integration/test_simpleactionplugin.py b/src/backend/InvenTree/plugin/samples/integration/test_simpleactionplugin.py index 57ee67bd4d..d48bceb924 100644 --- a/src/backend/InvenTree/plugin/samples/integration/test_simpleactionplugin.py +++ b/src/backend/InvenTree/plugin/samples/integration/test_simpleactionplugin.py @@ -27,7 +27,7 @@ class SimpleActionPluginTests(InvenTreeTestCase): self.set_plugin_state(False) 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) # Now enable the plugin