1313from flask import send_file
1414from flask import url_for
1515from marshmallow .exceptions import ValidationError
16- from openapi_spec_validator import validate_spec
17- from openapi_spec_validator .readers import read_from_filename
18- from openapi_spec_validator .validation .exceptions import OpenAPIValidationError
16+ from werkzeug .datastructures import Headers
1917from werkzeug .datastructures import MultiDict
2018
21- from .exceptions import FirstException
22- from .exceptions import FirstOpenAPIValidation
23- from .exceptions import FirstRequestArgsValidation
24- from .exceptions import FirstRequestCookieValidation
25- from .exceptions import FirstRequestJSONValidation
26- from .exceptions import FirstRequestPathArgsValidation
27- from .exceptions import FirstResponseJSONValidation
28- from .exceptions import FirstValidation
29- from .schema .tools import convert_schemas
30- from .schema .tools import resolving_refs
19+ from .first import RequestSerializer
20+ from .first import Specification
21+ from .first .exceptions import FirstException
22+ from .first .exceptions import FirstResponseJSONValidation
23+ from .first .exceptions import FirstValidation
3124
32- __version__ = '0.13.2 '
25+ __version__ = '0.14.1 '
3326
3427
3528class First :
@@ -50,15 +43,7 @@ def __init__(
5043 if self .app is not None :
5144 self .init_app (app )
5245
53- self .raw_spec , _ = read_from_filename (path_to_spec )
54- try :
55- validate_spec (self .raw_spec )
56- except OpenAPIValidationError as e :
57- raise FirstOpenAPIValidation (repr (e ))
58-
59- self .resolved_spec = resolving_refs (self .raw_spec )
60-
61- self .serialized_spec = convert_schemas (self .resolved_spec )
46+ self .spec = Specification (path_to_spec )
6247
6348 self ._mapped_routes_from_spec = []
6449
@@ -69,7 +54,7 @@ def route_to_openapi_format(route: str) -> str:
6954 def _route_registration_in_flask (self , func : callable ) -> None :
7055 route = method = ''
7156
72- for path , path_item in self .resolved_spec ['paths' ].items ():
57+ for path , path_item in self .spec . resolved_spec ['paths' ].items ():
7358 for method_name , operation in path_item .items ():
7459 if operation .get ('operationId' ) == func .__name__ :
7560 route : str = path
@@ -78,7 +63,7 @@ def _route_registration_in_flask(self, func: callable) -> None:
7863 if not route :
7964 raise FirstException (f'Route function <{ route } > not found in OpenAPI specification!' )
8065
81- params_schema = self .resolved_spec ['paths' ][route ][method ].get ('parameters' )
66+ params_schema = self .spec . resolved_spec ['paths' ][route ][method ].get ('parameters' )
8267
8368 if params_schema and '{' in route and '}' in route :
8469 path_params = re .findall (r'{(\S*?)}' , route )
@@ -98,14 +83,16 @@ def _route_registration_in_flask(self, func: callable) -> None:
9883
9984 def _extract_data_from_request (
10085 self , request_obj : Request
101- ) -> tuple [Any , str | None , dict [str , Any ] | None , dict , dict , Any | None ]:
86+ ) -> tuple [Any , str | None , Headers , dict [str , Any ] | None , dict , dict , Any | None ]:
10287 method = request_obj .method .lower ()
10388
10489 if request_obj .url_rule is not None :
10590 route = request_obj .url_rule .rule
10691 else :
10792 route = request_obj .url_rule
10893
94+ headers = request_obj .headers
95+
10996 view_args = request_obj .view_args
11097
11198 args = self ._resolved_params (request_obj .args )
@@ -117,7 +104,7 @@ def _extract_data_from_request(
117104 else :
118105 json = None
119106
120- return method , route , view_args , args , cookies , json
107+ return method , route , headers , view_args , args , cookies , json
121108
122109 def _resolved_params (self , payload : MultiDict ) -> dict :
123110 # payload.to_dict(flat=False) serializing all arguments as list for correct receipt of
@@ -171,69 +158,58 @@ def get_file_spec():
171158 def _register_request_validation (self ) -> None :
172159 @self .app .before_request
173160 def add_request_validating () -> None :
174- method , route , view_args , args , cookie , json = self ._extract_data_from_request (request )
175-
176- if route not in self ._mapped_routes_from_spec :
161+ if request .content_type != 'application/json' and request .method not in ('GET' ,):
177162 return
178163
179- if method in ('options ' ,):
164+ if request . method in ('OPTIONS ' ,):
180165 return
181166
182- route_as_in_spec = self .route_to_openapi_format (route )
167+ (
168+ method ,
169+ route ,
170+ headers ,
171+ view_args ,
172+ args ,
173+ cookies ,
174+ json ,
175+ ) = self ._extract_data_from_request (request )
183176
184- paths_schemas = self .serialized_spec ['paths' ]
185- method_schema = paths_schemas [route_as_in_spec ][method ]
186-
187- request .first_view_args = {}
188- request .first_args = {}
189- request .first_cookie = {}
177+ if route not in self ._mapped_routes_from_spec :
178+ return
190179
191- if view_args :
192- view_args_schema = method_schema ['parameters' ]['view_args' ]
193- try :
194- request .first_view_args = view_args_schema ().load (view_args )
195- except ValidationError as e :
196- raise FirstRequestPathArgsValidation (str (e ))
180+ route_as_in_spec = self .route_to_openapi_format (route )
197181
198- if args :
199- args_schema = method_schema ['parameters' ]['args' ]
200- try :
182+ params_schemas = self .spec .serialized_spec ['paths' ][route_as_in_spec ][method ].get (
183+ 'parameters'
184+ )
185+ if params_schemas :
186+ args_schema = params_schemas .get ('args' )
187+ if args_schema :
201188 schema_fields = args_schema ().fields
202- args_with_args_as_list = self ._arg_to_list (args , schema_fields )
203- request .first_args = args_schema ().load (args_with_args_as_list )
204- except ValidationError as e :
205- raise FirstRequestArgsValidation (str (e ))
206-
207- if cookie :
208- if 'parameters' in method_schema :
209- if 'cookie' in method_schema ['parameters' ]:
210- cookie_schema = method_schema ['parameters' ]['cookie' ]
211- try :
212- request .first_cookie = cookie_schema ().load (cookie )
213- except ValidationError as e :
214- raise FirstRequestCookieValidation (str (e ))
215-
216- if json :
217- content = method_schema ['requestBody' ]['content' ]
218- json_schema = content [request .content_type ]['schema' ]
219- try :
220- if isinstance (json , list ):
221- request .first_json = json_schema ._load (json , None )
222- elif 'allOf' in json_schema ._declared_fields :
223- request .first_json = json_schema ().load ({'allOf' : json })
224- elif 'anyOf' in json_schema ._declared_fields :
225- request .first_json = json_schema ().load ({'anyOf' : json })
226- elif 'oneOf' in json_schema ._declared_fields :
227- request .first_json = json_schema ().load ({'oneOf' : json })
228- else :
229- request .first_json = json_schema ().load (json )
230- except ValidationError as e :
231- raise FirstRequestJSONValidation (str (e ))
189+ args = self ._arg_to_list (args , schema_fields )
190+
191+ rv = RequestSerializer (
192+ self .spec ,
193+ method ,
194+ route_as_in_spec ,
195+ headers = dict (headers ),
196+ cookies = cookies ,
197+ path_params = view_args ,
198+ params = args ,
199+ json = json ,
200+ )
201+ rv .validate ()
202+
203+ request .first_headers = rv .serialized_headers
204+ request .first_view_args = rv .serialized_path_params
205+ request .first_args = rv .serialized_params
206+ request .first_cookies = rv .serialized_cookies
207+ request .first_json = rv .serialized_json
232208
233209 def _register_response_validation (self ) -> None :
234210 @self .app .after_request
235211 def add_response_validating (response : Response ) -> Response :
236- method , route , _ , _ , _ , json = self ._extract_data_from_request (request )
212+ method , route , _ , _ , _ , _ , json = self ._extract_data_from_request (request )
237213 json = response .get_json ()
238214
239215 if route not in self ._mapped_routes_from_spec :
@@ -242,7 +218,7 @@ def add_response_validating(response: Response) -> Response:
242218 route_as_in_spec = self .route_to_openapi_format (route )
243219
244220 try :
245- route_schema : dict = self .serialized_spec ['paths' ][route_as_in_spec ]
221+ route_schema : dict = self .spec . serialized_spec ['paths' ][route_as_in_spec ]
246222 except KeyError as e :
247223 raise FirstResponseJSONValidation (
248224 f'Route <{ e .args [0 ]} > not defined in specification.'
0 commit comments