Coverage for audoma/operations.py: 81%
64 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-08 06:12 +0000
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-08 06:12 +0000
1from dataclasses import dataclass
2from inspect import isclass
3from typing import (
4 Iterable,
5 List,
6 Type,
7 Union,
8)
10from rest_framework.exceptions import APIException
11from rest_framework.request import Request
12from rest_framework.response import Response
13from rest_framework.serializers import BaseSerializer
15from django.db.models import Model
16from django.views import View
19@dataclass
20class OperationExtractor:
22 collectors: Union[dict, Type[BaseSerializer]]
23 results: Union[dict, Type[BaseSerializer], str]
24 errors: List[Union[APIException, Type[APIException]]]
26 def _create_exception(self, options: dict) -> APIException:
27 """
28 Create proper exception if error status code, found defined in the response.
29 Args:
30 * options - kwargs for APIException instance.
31 Returns: APIException instance.
32 """
33 status_code = options.pop("status_code")
34 exception = APIException(**options)
35 exception.status_code = status_code if status_code else exception.status_code
36 return exception
38 def _extract_response_operation(
39 self, request: Request, code: int
40 ) -> Union[dict, Type[BaseSerializer], str, APIException]:
41 """
42 Extracts response operation from the defined in audoma_action dictionary.
44 Args:
45 * request - request object
46 * code - response status code, this should be passed as an integer.
47 Returns: Serializer instance, dictionary, string or APIException.
48 """
49 if code >= 400:
50 error_data = self.errors.get(code, {})
51 error_kwargs = {
52 "status_code": code,
53 "detail": error_data.get("detail"),
54 "code": error_data.get("error_code"),
55 }
56 return self._create_exception(error_kwargs)
58 if not self.results or (
59 isclass(self.results) and issubclass(self.results, BaseSerializer)
60 ):
61 return self.results
63 if isinstance(list(self.results.keys())[0], str) and request:
64 method = request.method.lower()
65 response = self.results.get(method)
66 if code and isinstance(response, dict):
67 response = response.get(code)
68 return response
70 elif isinstance(list(self.results.keys())[0], int) and code:
71 response = self.results.get(code)
72 return response
74 return None
76 def _extract_collect_operation(self, request: Request) -> Type[BaseSerializer]:
77 """
78 Extracts collect operation from the defined in audoma_action dictionary.
80 Args:
81 * request - request object
82 Returns: Serializer class.
83 """
84 if not self.collectors or (
85 isclass(self.collectors) and issubclass(self.collectors, BaseSerializer)
86 ):
87 return self.collectors
89 method = request.method.lower()
90 return self.collectors.get(method)
92 def extract_operation(
93 self, request: Request, code: int = None, operation_category="response"
94 ) -> Union[dict, Type[BaseSerializer], str, APIException]:
95 """
96 Extracts proper operation from the defined in audoma_action dictionary.
97 Args:
98 * request - request object
99 * code - response status code, this should be given only for responses.
100 It should be given as an integer
101 * operation_category - operation category, it can be either "response" or "collect".
102 This determines if the extracted operation should be either response or request operation.
103 Returns: Serializer instance, dictionary, string or APIException.
104 """
105 if operation_category == "response":
106 return self._extract_response_operation(request, code)
107 elif operation_category == "collect":
108 return self._extract_collect_operation(request)
109 else:
110 raise ValueError("Unknown operation_category")
113def apply_response_operation(
114 operation: Union[str, APIException, Type[BaseSerializer]],
115 instance: Union[Iterable, str, APIException, Model],
116 code: int,
117 view: View,
118 many: bool,
119) -> Response:
120 """
121 Applies response operation for automa_action decorator.
122 Args:
123 * operation: response operation wich will be applied, it may be a string message, APIException object, or
124 resoponse serializer_class instance
125 * instance: instance which will be returned in a response. It may be iterable, string, ApiException object,
126 or simply model instance. If it's dict/model instance it'll be serialized.
127 * code - response status code, this should be given as an integer
128 * view - view instance for which the response will be returned.
129 * many - boolean value which determines whether the serializer
130 will handle multiple instances, or just singular instnace.
132 Returns: Response object.
133 """
134 if isinstance(operation, APIException):
135 raise operation
137 if isinstance(operation, str):
138 instance = instance or operation
139 instance = {"message": instance}
140 return Response(instance, status=code)
142 serializer_class = operation
144 if instance:
145 if isinstance(instance, Iterable) and not isinstance(instance, dict):
146 serializer_kwargs = {"data": instance, "many": True}
147 else:
148 serializer_kwargs = {"instance": instance, "many": False}
149 else:
150 serializer_kwargs = {"many": many}
151 serializer_kwargs.update({"context": {"request": view.request}})
153 return_serializer = (
154 serializer_class(**serializer_kwargs)
155 if serializer_class
156 else view.get_result_serializer(**serializer_kwargs)
157 )
159 headers = view.get_success_headers(return_serializer.data)
161 return Response(return_serializer.data, status=code, headers=headers)