1"""
2This module defines an AbstractOperation class which implements an abstract Operation interface
3and functionality shared between Swagger 2 and OpenAPI 3 specifications.
4"""
5
6import abc
7import logging
8
9from connexion.utils import all_json
10
11logger = logging.getLogger("connexion.operations.abstract")
12
13DEFAULT_MIMETYPE = "application/json"
14
15
16class AbstractOperation(metaclass=abc.ABCMeta):
17
18 """
19 An API routes requests to an Operation by a (path, method) pair.
20 The operation uses a resolver to resolve its handler function.
21 We use the provided spec to do a bunch of heavy lifting before
22 (and after) we call security_schemes handler.
23 The registered handler function ends up looking something like::
24
25 @secure_endpoint
26 @validate_inputs
27 @deserialize_function_inputs
28 @serialize_function_outputs
29 @validate_outputs
30 def user_provided_handler_function(important, stuff):
31 if important:
32 serious_business(stuff)
33 """
34
35 def __init__(
36 self,
37 method,
38 path,
39 operation,
40 resolver,
41 app_security=None,
42 security_schemes=None,
43 randomize_endpoint=None,
44 uri_parser_class=None,
45 ):
46 """
47 :param method: HTTP method
48 :type method: str
49 :param path:
50 :type path: str
51 :param operation: swagger operation object
52 :type operation: dict
53 :param resolver: Callable that maps operationID to a function
54 :param app_security: list of security rules the application uses by default
55 :type app_security: list
56 :param security_schemes: `Security Definitions Object
57 <https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#security-definitions-object>`_
58 :type security_schemes: dict
59 :param randomize_endpoint: number of random characters to append to operation name
60 :type randomize_endpoint: integer
61 :param uri_parser_class: class to use for uri parsing
62 :type uri_parser_class: AbstractURIParser
63 """
64 self._method = method
65 self._path = path
66 self._operation = operation
67 self._resolver = resolver
68 self._security = operation.get("security", app_security)
69 self._security_schemes = security_schemes
70 self._uri_parser_class = uri_parser_class
71 self._randomize_endpoint = randomize_endpoint
72 self._operation_id = self._operation.get("operationId")
73
74 self._resolution = resolver.resolve(self)
75 self._operation_id = self._resolution.operation_id
76
77 self._responses = self._operation.get("responses", {})
78
79 @classmethod
80 @abc.abstractmethod
81 def from_spec(cls, spec, *args, path, method, resolver, **kwargs):
82 pass
83
84 @property
85 def method(self):
86 """
87 The HTTP method for this operation (ex. GET, POST)
88 """
89 return self._method
90
91 @property
92 def request_body(self):
93 """The request body for this operation"""
94
95 @property
96 def is_request_body_defined(self) -> bool:
97 """Whether the request body is defined for this operation"""
98 return self.request_body != {}
99
100 @property
101 def path(self):
102 """
103 The path of the operation, relative to the API base path
104 """
105 return self._path
106
107 @property
108 def security(self):
109 return self._security
110
111 @property
112 def security_schemes(self):
113 return self._security_schemes
114
115 @property
116 def responses(self):
117 """
118 Returns the responses for this operation
119 """
120 return self._responses
121
122 @property
123 def operation_id(self):
124 """
125 The operation id used to identify the operation internally to the app
126 """
127 return self._operation_id
128
129 @property
130 def randomize_endpoint(self):
131 """
132 number of random digits to generate and append to the operation_id.
133 """
134 return self._randomize_endpoint
135
136 @property
137 def router_controller(self):
138 """
139 The router controller to use (python module where handler functions live)
140 """
141 return self._router_controller
142
143 @property
144 @abc.abstractmethod
145 def parameters(self):
146 """
147 Returns the parameters for this operation
148 """
149
150 @property
151 @abc.abstractmethod
152 def produces(self):
153 """
154 Content-Types that the operation produces
155 """
156
157 @property
158 @abc.abstractmethod
159 def consumes(self):
160 """
161 Content-Types that the operation consumes
162 """
163
164 @abc.abstractmethod
165 def body_name(self, content_type: str) -> str:
166 """
167 Name of the body in the spec.
168 """
169
170 @abc.abstractmethod
171 def body_schema(self, content_type: str = None) -> dict:
172 """
173 The body schema definition for this operation.
174 """
175
176 @abc.abstractmethod
177 def body_definition(self, content_type: str = None) -> dict:
178 """
179 The body definition for this operation.
180 :rtype: dict
181 """
182
183 def response_definition(self, status_code=None, content_type=None):
184 """
185 response definition for this endpoint
186 """
187 response_definition = self.responses.get(
188 str(status_code), self.responses.get("default", {})
189 )
190 return response_definition
191
192 @abc.abstractmethod
193 def response_schema(self, status_code=None, content_type=None):
194 """
195 response schema for this endpoint
196 """
197
198 @abc.abstractmethod
199 def example_response(self, status_code=None, content_type=None):
200 """
201 Returns an example from the spec
202 """
203
204 @abc.abstractmethod
205 def get_path_parameter_types(self):
206 """
207 Returns the types for parameters in the path
208 """
209
210 @abc.abstractmethod
211 def with_definitions(self, schema):
212 """
213 Returns the given schema, but with the definitions from the spec
214 attached. This allows any remaining references to be resolved by a
215 validator (for example).
216 """
217
218 def get_mimetype(self):
219 """
220 If the endpoint has no 'produces' then the default is
221 'application/json'.
222
223 :rtype str
224 """
225 # TODO: don't default
226 if all_json(self.produces):
227 try:
228 return self.produces[0]
229 except IndexError:
230 return DEFAULT_MIMETYPE
231 elif len(self.produces) == 1:
232 return self.produces[0]
233 else:
234 return DEFAULT_MIMETYPE
235
236 @property
237 def uri_parser_class(self):
238 """
239 The uri parser class for this operation.
240 """
241 return self._uri_parser_class
242
243 @property
244 def function(self):
245 """
246 Resolved function.
247
248 :rtype: types.FunctionType
249 """
250 return self._resolution.function