16
16
from openapi_test_client .clients import APIClientType
17
17
from openapi_test_client .libraries .api import Endpoint
18
18
19
-
20
19
logger = get_logger (__name__ )
21
20
22
21
@@ -57,7 +56,7 @@ def get_api_spec(self, url: str | None = None) -> dict[str, Any] | None:
57
56
elif not (open_api_version := api_spec ["openapi" ]).startswith ("3." ):
58
57
raise NotImplementedError (f"Unsupported OpenAPI version: { open_api_version } " )
59
58
except Exception as e :
60
- logger .error (f"Unable to get API specs from { url } \n { type (e ).__name__ } : { e } " )
59
+ logger .exception (f"Unable to get API specs from { url } \n { type (e ).__name__ } " , exc_info = e )
61
60
else :
62
61
self ._spec = OpenAPISpec .parse (api_spec )
63
62
return self ._spec
@@ -113,7 +112,17 @@ def _resolve_schemas(api_spec: dict[str, Any]) -> dict[str, Any]:
113
112
ref_pattern = re .compile (r"#?/([^/]+)" )
114
113
115
114
def has_reference (obj : Any ) -> bool :
116
- return "'$ref':" in str (obj )
115
+ if isinstance (obj , dict ):
116
+ for key , value in obj .items ():
117
+ if key == "$ref" :
118
+ return True
119
+ if has_reference (value ):
120
+ return True
121
+ elif isinstance (obj , list ):
122
+ for item in obj :
123
+ if has_reference (item ):
124
+ return True
125
+ return False
117
126
118
127
def resolve_recursive (reference : Any , schemas_seen : list [str ] | None = None ):
119
128
if schemas_seen is None :
@@ -122,32 +131,38 @@ def resolve_recursive(reference: Any, schemas_seen: list[str] | None = None):
122
131
for k , v in copy .deepcopy (reference ).items ():
123
132
new_reference = reference [k ]
124
133
if k == "$ref" :
134
+ del reference [k ]
125
135
ref_keys = re .findall (ref_pattern , new_reference )
126
136
assert ref_keys
127
137
schema = "/" .join (ref_keys )
128
138
if schema in schemas_seen :
139
+ # Detected a circular reference
129
140
logger .warning (
130
- f"WARNING: Detected recursive schema definition . This is not supported: { schema } "
141
+ f"WARNING: Detected a circular schema reference . This is not supported: { schema } "
131
142
)
143
+ reference .clear ()
144
+ reference .update (type = "object" )
132
145
else :
133
146
schemas_seen .append (schema )
134
- try :
135
- resolved_value = reduce (lambda d , k : d [k ], ref_keys , api_spec )
136
- del reference [k ]
137
- except KeyError as e :
138
- logger .warning (f"SKIPPED: Unable to resolve '$ref' for '{ new_reference } ' (KeyError: { e } )" )
139
- else :
140
- if has_reference (resolved_value ):
141
- resolved_value = resolve_recursive (resolved_value , schemas_seen = schemas_seen )
142
- if isinstance (resolved_value , dict ):
143
- reference .update (resolved_value )
147
+ try :
148
+ resolved_value = reduce (lambda d , k : d [k ], ref_keys , api_spec )
149
+ except KeyError as e :
150
+ logger .warning (
151
+ f"SKIPPED: Unable to resolve '$ref' for '{ new_reference } ' (KeyError: { e } )"
152
+ )
144
153
else :
145
- reference = resolved_value
154
+ if has_reference (resolved_value ):
155
+ resolved_value = resolve_recursive (resolved_value , schemas_seen = schemas_seen )
156
+ schemas_seen .remove (schema )
157
+ if isinstance (resolved_value , dict ):
158
+ reference .update (resolved_value )
159
+ else :
160
+ reference = resolved_value
146
161
else :
147
- resolve_recursive (new_reference )
162
+ resolve_recursive (new_reference , schemas_seen = schemas_seen )
148
163
elif isinstance (reference , list ):
149
164
for item in reference :
150
- resolve_recursive (item )
165
+ resolve_recursive (item , schemas_seen = schemas_seen )
151
166
return reference
152
167
153
168
if has_reference (api_spec ):
0 commit comments