1
+ import re
2
+ from collections import OrderedDict
1
3
from copy import deepcopy
2
4
from dataclasses import dataclass , field
3
5
from typing import Any , Dict , Iterator , List , Optional , Set , Tuple , Union
13
15
from .properties import Class , EnumProperty , ModelProperty , Property , Schemas , build_schemas , property_from_data
14
16
from .responses import Response , response_from_data
15
17
18
+ _PATH_PARAM_REGEX = re .compile ("{([a-zA-Z_][a-zA-Z0-9_]*)}" )
19
+
16
20
17
21
def import_string_from_class (class_ : Class , prefix : str = "" ) -> str :
18
22
"""Create a string which is used to import a reference"""
@@ -48,10 +52,11 @@ def from_data(
48
52
)
49
53
# Add `PathItem` parameters
50
54
if not isinstance (endpoint , ParseError ):
51
- endpoint , schemas = Endpoint ._add_parameters (
55
+ endpoint , schemas = Endpoint .add_parameters (
52
56
endpoint = endpoint , data = path_data , schemas = schemas , config = config
53
57
)
54
-
58
+ if not isinstance (endpoint , ParseError ):
59
+ endpoint = Endpoint .sort_parameters (endpoint = endpoint )
55
60
if isinstance (endpoint , ParseError ):
56
61
endpoint .header = (
57
62
f"ERROR parsing { method .upper ()} { path } within { tag } . Endpoint will not be generated."
@@ -91,7 +96,7 @@ class Endpoint:
91
96
summary : Optional [str ] = ""
92
97
relative_imports : Set [str ] = field (default_factory = set )
93
98
query_parameters : Dict [str , Property ] = field (default_factory = dict )
94
- path_parameters : Dict [str , Property ] = field (default_factory = dict )
99
+ path_parameters : "OrderedDict [str, Property]" = field (default_factory = OrderedDict )
95
100
header_parameters : Dict [str , Property ] = field (default_factory = dict )
96
101
cookie_parameters : Dict [str , Property ] = field (default_factory = dict )
97
102
responses : List [Response ] = field (default_factory = list )
@@ -240,7 +245,7 @@ def _add_responses(
240
245
return endpoint , schemas
241
246
242
247
@staticmethod
243
- def _add_parameters (
248
+ def add_parameters (
244
249
* , endpoint : "Endpoint" , data : Union [oai .Operation , oai .PathItem ], schemas : Schemas , config : Config
245
250
) -> Tuple [Union ["Endpoint" , ParseError ], Schemas ]:
246
251
endpoint = deepcopy (endpoint )
@@ -259,6 +264,9 @@ def _add_parameters(
259
264
if isinstance (param , oai .Reference ) or param .param_schema is None :
260
265
continue
261
266
267
+ if param .param_in == oai .ParameterLocation .PATH and not param .required :
268
+ return ParseError (data = param , detail = "Path parameter must be required" ), schemas
269
+
262
270
unique_param = (param .name , param .param_in )
263
271
if unique_param in unique_parameters :
264
272
duplication_detail = (
@@ -282,6 +290,7 @@ def _add_parameters(
282
290
if prop .name in parameters_by_location [param .param_in ]:
283
291
# This parameter was defined in the Operation, so ignore the PathItem definition
284
292
continue
293
+
285
294
for location , parameters_dict in parameters_by_location .items ():
286
295
if location == param .param_in or prop .name not in parameters_dict :
287
296
continue
@@ -318,6 +327,24 @@ def _add_parameters(
318
327
319
328
return endpoint , schemas
320
329
330
+ @staticmethod
331
+ def sort_parameters (* , endpoint : "Endpoint" ) -> Union ["Endpoint" , ParseError ]:
332
+ endpoint = deepcopy (endpoint )
333
+ parameters_from_path = re .findall (_PATH_PARAM_REGEX , endpoint .path )
334
+ try :
335
+ sorted_params = sorted (
336
+ endpoint .path_parameters .values (), key = lambda param : parameters_from_path .index (param .name )
337
+ )
338
+ endpoint .path_parameters = OrderedDict ((param .name , param ) for param in sorted_params )
339
+ except ValueError :
340
+ pass # We're going to catch the difference down below
341
+ path_parameter_names = [name for name in endpoint .path_parameters ]
342
+ if parameters_from_path != path_parameter_names :
343
+ return ParseError (
344
+ detail = f"Incorrect path templating for { endpoint .path } (Path parameters do not match with path)" ,
345
+ )
346
+ return endpoint
347
+
321
348
@staticmethod
322
349
def from_data (
323
350
* , data : oai .Operation , path : str , method : str , tag : str , schemas : Schemas , config : Config
@@ -339,7 +366,7 @@ def from_data(
339
366
tag = tag ,
340
367
)
341
368
342
- result , schemas = Endpoint ._add_parameters (endpoint = endpoint , data = data , schemas = schemas , config = config )
369
+ result , schemas = Endpoint .add_parameters (endpoint = endpoint , data = data , schemas = schemas , config = config )
343
370
if isinstance (result , ParseError ):
344
371
return result , schemas
345
372
result , schemas = Endpoint ._add_responses (endpoint = result , data = data .responses , schemas = schemas , config = config )
0 commit comments