1+ import hashlib
12import json
23import pathlib
4+ import random
5+ import uuid
36import yaml
7+
48from jsonref import JsonRef
59from yaml import CSafeLoader
610
@@ -58,7 +62,12 @@ def type_to_python(schema, alternative_name=None):
5862 return (
5963 alternative_name
6064 if alternative_name
61- and ("properties" in schema or "oneOf" in schema or "anyOf" in schema or "allOf" in schema )
65+ and (
66+ "properties" in schema
67+ or "oneOf" in schema
68+ or "anyOf" in schema
69+ or "allOf" in schema
70+ )
6271 else "dict"
6372 )
6473 elif type_ == "null" :
@@ -70,7 +79,9 @@ def type_to_python(schema, alternative_name=None):
7079def get_type_for_attribute (schema , attribute , current_name = None ):
7180 """Return Python type name for the attribute."""
7281 child_schema = schema .get ("properties" , {}).get (attribute )
73- alternative_name = current_name + formatter .camel_case (attribute ) if current_name else None
82+ alternative_name = (
83+ current_name + formatter .camel_case (attribute ) if current_name else None
84+ )
7485 return type_to_python (child_schema , alternative_name = alternative_name )
7586
7687
@@ -155,7 +166,9 @@ def child_models(schema, alternative_name=None, seen=None):
155166 yield name , schema
156167
157168 for key , child in schema .get ("properties" , {}).items ():
158- yield from child_models (child , alternative_name = name + formatter .camel_case (key ), seen = seen )
169+ yield from child_models (
170+ child , alternative_name = name + formatter .camel_case (key ), seen = seen
171+ )
159172
160173 if current_name and schema .get ("type" ) == "array" :
161174 if name in seen :
@@ -211,7 +224,11 @@ def get_references_for_model(model, model_name):
211224 result = []
212225 top_name = formatter .get_name (model ) or model_name
213226 for key , definition in model .get ("properties" , {}).items ():
214- if definition .get ("type" ) == "object" or definition .get ("enum" ) or definition .get ("oneOf" ):
227+ if (
228+ definition .get ("type" ) == "object"
229+ or definition .get ("enum" )
230+ or definition .get ("oneOf" )
231+ ):
215232 name = formatter .get_name (definition )
216233 if name :
217234 result .append (name )
@@ -322,7 +339,8 @@ def get_api_models(operations):
322339 break
323340 for content in operation .get ("parameters" , []):
324341 if "schema" in content and (
325- content ["schema" ].get ("type" ) in ("object" , "array" ) or content ["schema" ].get ("enum" )
342+ content ["schema" ].get ("type" ) in ("object" , "array" )
343+ or content ["schema" ].get ("enum" )
326344 ):
327345 name = formatter .get_name (content ["schema" ])
328346 if name and name not in seen :
@@ -349,7 +367,9 @@ def parameters(operation):
349367
350368 if "requestBody" in operation :
351369 if "multipart/form-data" in operation ["requestBody" ]["content" ]:
352- parent = operation ["requestBody" ]["content" ]["multipart/form-data" ]["schema" ]
370+ parent = operation ["requestBody" ]["content" ]["multipart/form-data" ][
371+ "schema"
372+ ]
353373 for name , schema in parent ["properties" ].items ():
354374 yield name , {
355375 "in" : "form" ,
@@ -412,3 +432,140 @@ def collection_format(parameter):
412432 style = parameter .get ("style" , in_to_style [in_ ])
413433 explode = parameter .get ("explode" , True if style == "form" else False )
414434 return matrix .get ((style , explode ), "multi" )
435+
436+
437+ def generate_value (schema , use_random = False , prefix = None ):
438+ spec = schema .spec
439+ if not use_random :
440+ if "example" in spec :
441+ return spec ["example" ]
442+ if "default" in spec :
443+ return spec ["default" ]
444+
445+ if spec ["type" ] == "string" :
446+ if use_random :
447+ return str (
448+ uuid .UUID (
449+ bytes = hashlib .sha256 (
450+ str (prefix or schema .keys ).encode ("utf-8" ),
451+ ).digest ()[:16 ]
452+ )
453+ )
454+ return "string"
455+ elif spec ["type" ] == "integer" :
456+ return (
457+ random .randint (0 , 32000 ) if use_random else len (str (prefix or schema .keys ))
458+ )
459+ elif spec ["type" ] == "number" :
460+ return random .random () if use_random else 1.0 / len (str (prefix or schema .keys ))
461+ elif spec ["type" ] == "boolean" :
462+ return True
463+ elif spec ["type" ] == "array" :
464+ return [generate_value (schema [0 ], use_random = use_random )]
465+ elif spec ["type" ] == "object" :
466+ return {
467+ key : generate_value (schema [key ], use_random = use_random )
468+ for key in spec ["properties" ]
469+ }
470+ else :
471+ raise TypeError (f"Unknown type: { spec ['type' ]} " )
472+
473+
474+ class Schema :
475+ def __init__ (self , spec , value = None , keys = None ):
476+ self .spec = spec
477+ self .value = value if value is not None else generate_value
478+ self .keys = keys or tuple ()
479+
480+ def __getattr__ (self , key ):
481+ return self [key ]
482+
483+ def __getitem__ (self , key ):
484+ type_ = self .spec .get ("type" , "object" )
485+ if type_ == "object" :
486+ try :
487+ return self .__class__ (
488+ self .spec ["properties" ][key ],
489+ value = self .value ,
490+ keys = self .keys + (key ,),
491+ )
492+ except KeyError :
493+ if "oneOf" in self .spec :
494+ for schema in self .spec ["oneOf" ]:
495+ if schema .get ("type" , "object" ) == "object" :
496+ try :
497+ return self .__class__ (
498+ schema ["properties" ][key ],
499+ value = self .value ,
500+ keys = self .keys + (key ,),
501+ )
502+ except KeyError :
503+ pass
504+ raise KeyError (
505+ f"{ key } not found in { self .spec .get ('properties' , {}).keys ()} : { self .spec } "
506+ )
507+ if type_ == "array" :
508+ return self .__class__ (
509+ self .spec ["items" ], value = self .value , keys = self .keys + (key ,)
510+ )
511+
512+ raise KeyError (f"{ key } not found in { self .spec } " )
513+
514+ def __repr__ (self ):
515+ value = self .value (self )
516+ if isinstance (value , (dict , list )):
517+ return json .dumps (value , indent = 2 )
518+ return str (value )
519+
520+
521+ class Operation :
522+ def __init__ (self , name , spec , method , path ):
523+ self .name = name
524+ self .spec = spec
525+ self .method = method
526+ self .path = path
527+
528+ def server_url_and_method (self , spec , server_index = 0 , server_variables = None ):
529+ def format_server (server , path ):
530+ url = server ["url" ] + path
531+ # replace potential path variables
532+ for variable , value in server_variables .items ():
533+ url = url .replace ("{" + variable + "}" , value )
534+ # replace server variables if they were not replace before
535+ for variable in server ["variables" ]:
536+ if variable in server_variables :
537+ continue
538+ url = url .replace (
539+ "{" + variable + "}" , server ["variables" ][variable ]["default" ]
540+ )
541+ return url
542+
543+ server_variables = server_variables or {}
544+ if "servers" in self .spec :
545+ server = self .spec ["servers" ][server_index ]
546+ else :
547+ server = spec ["servers" ][server_index ]
548+ return format_server (server , self .path ), self .method
549+
550+ def response_code_and_accept_type (self ):
551+ for response in self .spec ["responses" ]:
552+ return int (response ), next (
553+ iter (self .spec ["responses" ][response ].get ("content" , {None : None }))
554+ )
555+ return None , None
556+
557+ def request_content_type (self ):
558+ return next (iter (self .spec .get ("requestBody" , {}).get ("content" , {None : None })))
559+
560+ def response (self ):
561+ for response in self .spec ["responses" ]:
562+ return Schema (
563+ next (iter ((self .spec ["responses" ][response ]["content" ].values ())))[
564+ "schema"
565+ ]
566+ )
567+
568+ def request (self ):
569+ return Schema (
570+ next (iter (self .spec ["requestBody" ]["content" ].values ()))["schema" ]
571+ )
0 commit comments