23
23
]
24
24
25
25
26
+ def path_to_operation_id (path : str , http_method : str = "get" ) -> str :
27
+ """
28
+ Converts a path to an operationId.
29
+
30
+ :param path: The path to convert.
31
+ :param http_method: The HTTP method to use for the operationId.
32
+ :returns: The operationId.
33
+ """
34
+ if http_method .lower () not in VALID_HTTP_METHODS :
35
+ raise ValueError (f"Invalid HTTP method: { http_method } " )
36
+ return path .replace ("/" , "_" ).lstrip ("_" ).rstrip ("_" ) + "_" + http_method .lower ()
37
+
38
+
26
39
class LLMProvider (Enum ):
27
40
"""
28
41
LLM providers supported by `OpenAPITool`.
29
42
"""
43
+
30
44
OPENAI = "openai"
31
45
ANTHROPIC = "anthropic"
32
46
COHERE = "cohere"
@@ -50,18 +64,18 @@ def from_str(string: str) -> "LLMProvider":
50
64
@dataclass
51
65
class Operation :
52
66
"""
53
- Represents an operation in an OpenAPI specification
54
-
55
- See https://spec.openapis.org/oas/latest.html#paths-object for details.
56
- Path objects can contain multiple operations, each with a unique combination of path and method.
57
-
58
- :param path: Path of the operation.
59
- :param method: HTTP method of the operation.
60
- :param operation_dict: Operation details from OpenAPI spec
61
- :param spec_dict: The encompassing OpenAPI specification.
62
- :param security_requirements: A list of security requirements for the operation.
63
- :param request_body: Request body details.
64
- :param parameters: Parameters for the operation.
67
+ Represents an operation in an OpenAPI specification
68
+
69
+ See https://spec.openapis.org/oas/latest.html#paths-object for details.
70
+ Path objects can contain multiple operations, each with a unique combination of path and method.
71
+
72
+ :param path: Path of the operation.
73
+ :param method: HTTP method of the operation.
74
+ :param operation_dict: Operation details from OpenAPI spec
75
+ :param spec_dict: The encompassing OpenAPI specification.
76
+ :param security_requirements: A list of security requirements for the operation.
77
+ :param request_body: Request body details.
78
+ :param parameters: Parameters for the operation.
65
79
"""
66
80
67
81
path : str
@@ -105,8 +119,12 @@ def get_server(self, server_index: int = 0) -> str:
105
119
:returns: The server URL.
106
120
:raises ValueError: If no servers are found in the specification.
107
121
"""
108
- servers = self .operation_dict .get ("servers" , []) or self .spec_dict .get (
109
- "servers" , []
122
+ # servers can be defined at the operation level, path level, or at the root level
123
+ # search for servers in the following order: operation, path, root
124
+ servers = (
125
+ self .operation_dict .get ("servers" , [])
126
+ or self .spec_dict .get ("paths" , {}).get (self .path , {}).get ("servers" , [])
127
+ or self .spec_dict .get ("servers" , [])
110
128
)
111
129
if not servers :
112
130
raise ValueError ("No servers found in the provided specification." )
@@ -136,11 +154,7 @@ def __init__(self, spec_dict: Dict[str, Any]):
136
154
f"Invalid OpenAPI specification, expected a dictionary: { spec_dict } "
137
155
)
138
156
# just a crude sanity check, by no means a full validation
139
- if (
140
- "openapi" not in spec_dict
141
- or "paths" not in spec_dict
142
- or "servers" not in spec_dict
143
- ):
157
+ if "openapi" not in spec_dict or "paths" not in spec_dict :
144
158
raise ValueError (
145
159
"Invalid OpenAPI specification format. See https://swagger.io/specification/ for details." ,
146
160
spec_dict ,
@@ -201,51 +215,30 @@ def from_url(cls, url: str) -> "OpenAPISpecification":
201
215
) from e
202
216
return cls .from_str (content )
203
217
204
- def find_operation_by_id (
205
- self , op_id : str , method : Optional [str ] = None
206
- ) -> Operation :
218
+ def find_operation_by_id (self , op_id : str ) -> Operation :
207
219
"""
208
220
Find an Operation by operationId.
209
221
210
222
:param op_id: The operationId of the operation.
211
- :param method: The HTTP method of the operation.
212
223
:returns: The matching operation
213
224
:raises ValueError: If no operation is found with the given operationId.
214
225
"""
215
- for path , path_item in self .spec_dict .get ("paths" , {}).items ():
216
- op : Operation = self .get_operation_item (path , path_item , method )
217
- if op_id in op .operation_dict .get ("operationId" , "" ):
218
- return self .get_operation_item (path , path_item , method )
219
- raise ValueError (
220
- f"No operation found with operationId { op_id } , method { method } "
221
- )
222
-
223
- def get_operation_item (
224
- self , path : str , path_item : Dict [str , Any ], method : Optional [str ] = None
225
- ) -> Operation :
226
- """
227
- Gets a particular Operation item from the OpenAPI specification given the path and method.
228
-
229
- :param path: The path of the operation.
230
- :param path_item: The path item from the OpenAPI specification.
231
- :param method: The HTTP method of the operation.
232
- :returns: The operation
233
- """
234
- if method :
235
- operation_dict = path_item .get (method .lower (), {})
236
- if not operation_dict :
237
- raise ValueError (
238
- f"No operation found for method { method } at path { path } "
239
- )
240
- return Operation (path , method .lower (), operation_dict , self .spec_dict )
241
- if len (path_item ) == 1 :
242
- method , operation_dict = next (iter (path_item .items ()))
243
- return Operation (path , method , operation_dict , self .spec_dict )
244
- if len (path_item ) > 1 :
245
- raise ValueError (
246
- f"Multiple operations found at path { path } , method parameter is required."
247
- )
248
- raise ValueError (f"No operations found at path { path } and method { method } " )
226
+ for path , path_value in self .spec_dict .get ("paths" , {}).items ():
227
+ operations = {
228
+ method : operation_dict
229
+ for method , operation_dict in path_value .items ()
230
+ if method .lower () in VALID_HTTP_METHODS
231
+ }
232
+
233
+ for method , operation_dict in operations .items ():
234
+ if (
235
+ operation_dict .get (
236
+ "operationId" , path_to_operation_id (path , method )
237
+ )
238
+ == op_id
239
+ ):
240
+ return Operation (path , method , operation_dict , self .spec_dict )
241
+ raise ValueError (f"No operation found with operationId { op_id } " )
249
242
250
243
def get_security_schemes (self ) -> Dict [str , Dict [str , Any ]]:
251
244
"""
0 commit comments