Coverage for src/backend/InvenTree/plugin/base/action/api.py: 76%

32 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-07 17:47 +0000

1"""APIs for action plugins.""" 

2 

3from django.utils.translation import gettext_lazy as _ 

4 

5from drf_spectacular.utils import OpenApiResponse, extend_schema 

6from rest_framework import serializers, status 

7from rest_framework.generics import GenericAPIView 

8from rest_framework.response import Response 

9 

10import InvenTree.permissions 

11from InvenTree.exceptions import log_error 

12from plugin import PluginMixinEnum, registry 

13 

14 

15class ActionPluginSerializer(serializers.Serializer): 

16 """Serializer for the ActionPluginView responses.""" 

17 

18 action = serializers.CharField() 

19 data = serializers.DictField() 

20 

21 

22class ActionPluginErrorSerializer(serializers.Serializer): 

23 """Serializer for the ActionPluginView error responses.""" 

24 

25 error = serializers.CharField() 

26 action = serializers.CharField(required=False) 

27 

28 

29class ActionPluginView(GenericAPIView): 

30 """Endpoint for running custom action plugins.""" 

31 

32 permission_classes = [InvenTree.permissions.IsAuthenticatedOrReadScope] 

33 serializer_class = ActionPluginSerializer 

34 

35 @extend_schema( 

36 responses={ 

37 200: ActionPluginSerializer, 

38 400: OpenApiResponse( 

39 description='No action specified', 

40 response=ActionPluginErrorSerializer(), 

41 ), 

42 404: OpenApiResponse( 

43 description='No matching action found', 

44 response=ActionPluginErrorSerializer(), 

45 ), 

46 } 

47 ) 

48 def post(self, request, *args, **kwargs): 

49 """This function checks if all required info was submitted and then performs a plugin_action or returns an error.""" 

50 action = request.data.get('action', None) 

51 

52 data = request.data.get('data', None) 

53 

54 if action is None: 

55 return Response( 

56 {'error': _('No action specified')}, status=status.HTTP_400_BAD_REQUEST 

57 ) 

58 

59 action_plugins = registry.with_mixin(PluginMixinEnum.ACTION) 

60 for plugin in action_plugins: 60 ↛ 61line 60 didn't jump to line 61 because the loop on line 60 never started

61 try: 

62 if plugin.action_name() == action: 

63 plugin.perform_action(request.user, data=data) 

64 return Response(plugin.get_response(request.user, data=data)) 

65 except Exception: 

66 log_error('perform_action', plugin=plugin.slug) 

67 

68 # If we got to here, no matching action was found 

69 return Response( 

70 {'error': _('No matching action found'), 'action': action}, 

71 status=status.HTTP_404_NOT_FOUND, 

72 )