1+ import  copy 
2+ import  json 
13import  os 
24import  re 
35from  collections  import  defaultdict 
46from  datetime  import  datetime 
7+ from  typing  import  Any , Dict , Optional , Tuple 
58import  yaml 
69from  hackingBuddyGPT .capabilities .yamlFile  import  YAMLFile 
7- from  hackingBuddyGPT .usecases . web_api_testing . documentation .pattern_matcher  import  PatternMatcher 
10+ from  hackingBuddyGPT .utils . web_api .pattern_matcher  import  PatternMatcher 
811from  hackingBuddyGPT .utils .prompt_generation .information  import  PromptStrategy 
9- from  hackingBuddyGPT .usecases .web_api_testing .response_processing  import  ResponseHandler 
10- from  hackingBuddyGPT .usecases .web_api_testing .utils  import  LLMHandler 
12+ from  hackingBuddyGPT .utils .web_api .llm_handler  import  LLMHandler 
1113
1214
1315class  OpenAPISpecificationHandler (object ):
1416    """ 
1517    Handles the generation and updating of an OpenAPI specification document based on dynamic API responses. 
1618
1719    Attributes: 
18-         response_handler (object): An instance of the response handler for processing API responses. 
1920        schemas (dict): A dictionary to store API schemas. 
2021        filename (str): The filename for the OpenAPI specification file. 
2122        openapi_spec (dict): The OpenAPI specification document structure. 
@@ -26,18 +27,16 @@ class OpenAPISpecificationHandler(object):
2627        _capabilities (dict): A dictionary to store capabilities related to YAML file handling. 
2728    """ 
2829
29-     def  __init__ (self , llm_handler : LLMHandler , response_handler :  ResponseHandler ,  strategy : PromptStrategy , url : str ,
30+     def  __init__ (self , llm_handler : LLMHandler , strategy : PromptStrategy , url : str ,
3031                 description : str , name : str ) ->  None :
3132        """ 
3233        Initializes the handler with a template OpenAPI specification. 
3334
3435        Args: 
3536            llm_handler (object): An instance of the LLM handler for interacting with the LLM. 
36-             response_handler (object): An instance of the response handler for processing API responses. 
3737            strategy (PromptStrategy): An instance of the PromptStrategy class. 
3838        """ 
3939        self .unsuccessful_methods  =  {}
40-         self .response_handler  =  response_handler 
4140        self .schemas  =  {}
4241        self .query_params  =  {}
4342        self .endpoint_methods  =  {}
@@ -103,6 +102,143 @@ def is_partial_match(self, element, string_list):
103102
104103        return  False 
105104
105+     def  parse_http_response_to_openapi_example (
106+             self , openapi_spec : Dict [str , Any ], http_response : str , path : str , method : str 
107+     ) ->  Tuple [Optional [Dict [str , Any ]], Optional [str ], Dict [str , Any ]]:
108+         """ 
109+         Parses an HTTP response to generate an OpenAPI example. 
110+ 
111+         Args: 
112+             openapi_spec (Dict[str, Any]): The OpenAPI specification to update. 
113+             http_response (str): The HTTP response to parse. 
114+             path (str): The API path. 
115+             method (str): The HTTP method. 
116+ 
117+         Returns: 
118+             Tuple[Optional[Dict[str, Any]], Optional[str], Dict[str, Any]]: A tuple containing the entry dictionary, reference, and updated OpenAPI specification. 
119+         """ 
120+ 
121+         headers , body  =  http_response .split ("\r \n \r \n " , 1 )
122+         try :
123+             body_dict  =  json .loads (body )
124+         except  json .decoder .JSONDecodeError :
125+             return  None , None , openapi_spec 
126+ 
127+         reference , object_name , openapi_spec  =  self .parse_http_response_to_schema (openapi_spec , body_dict , path )
128+         entry_dict  =  {}
129+         old_body_dict  =  copy .deepcopy (body_dict )
130+ 
131+         if  len (body_dict ) ==  1  and  "data"  not  in body_dict :
132+             entry_dict ["id" ] =  body_dict 
133+             self .llm_handler ._add_created_object (entry_dict , object_name )
134+         else :
135+             if  "data"  in  body_dict :
136+                 body_dict  =  body_dict ["data" ]
137+                 if  isinstance (body_dict , list ) and  len (body_dict ) >  0 :
138+                     body_dict  =  body_dict [0 ]
139+                     if  isinstance (body_dict , list ):
140+                         for  entry  in  body_dict :
141+                             key  =  entry .get ("title" ) or  entry .get ("name" ) or  entry .get ("id" )
142+                             entry_dict [key ] =  {"value" : entry }
143+                             self .llm_handler ._add_created_object (entry_dict [key ], object_name )
144+                             if  len (entry_dict ) >  3 :
145+                                 break 
146+ 
147+ 
148+             if  isinstance (body_dict , list ) and  len (body_dict ) >  0 :
149+                 body_dict  =  body_dict [0 ]
150+                 if  isinstance (body_dict , list ):
151+ 
152+                     for  entry  in  body_dict :
153+                         key  =  entry .get ("title" ) or  entry .get ("name" ) or  entry .get ("id" )
154+                         entry_dict [key ] =  entry 
155+                         self .llm_handler ._add_created_object (entry_dict [key ], object_name )
156+                         if  len (entry_dict ) >  3 :
157+                             break 
158+             else :
159+                 if  isinstance (body_dict , list ) and  len (body_dict ) ==  0 :
160+                     entry_dict  =  "" 
161+                 elif  isinstance (body_dict , dict ) and  "data"  in  body_dict .keys ():
162+                     entry_dict  =  body_dict ["data" ]
163+                     if  isinstance (entry_dict , list ) and  len (entry_dict ) >  0 :
164+                         entry_dict  =  entry_dict [0 ]
165+                 else :
166+                     entry_dict =  body_dict 
167+                 self .llm_handler ._add_created_object (entry_dict , object_name )
168+         if  isinstance (old_body_dict , dict ) and  len (old_body_dict .keys ()) >  0  and  "data"  in  old_body_dict .keys () and  isinstance (old_body_dict , dict ) \
169+                 and  isinstance (entry_dict , dict ):
170+             old_body_dict .pop ("data" )
171+             entry_dict  =  {** entry_dict , ** old_body_dict }
172+ 
173+ 
174+         return  entry_dict , reference , openapi_spec 
175+ 
176+     def  parse_http_response_to_schema (
177+             self , openapi_spec : Dict [str , Any ], body_dict : Dict [str , Any ], path : str 
178+     ) ->  Tuple [str , str , Dict [str , Any ]]:
179+         """ 
180+         Parses an HTTP response body to generate an OpenAPI schema. 
181+ 
182+         Args: 
183+             openapi_spec (Dict[str, Any]): The OpenAPI specification to update. 
184+             body_dict (Dict[str, Any]): The HTTP response body as a dictionary or list. 
185+             path (str): The API path. 
186+ 
187+         Returns: 
188+             Tuple[str, str, Dict[str, Any]]: A tuple containing the reference, object name, and updated OpenAPI specification. 
189+         """ 
190+         if  "/"  not  in path :
191+             return  None , None , openapi_spec 
192+ 
193+         object_name  =  path .split ("/" )[1 ].capitalize ().rstrip ("s" )
194+         properties_dict  =  {}
195+ 
196+         # Handle different structures of `body_dict` 
197+         if  isinstance (body_dict , dict ):
198+             for  key , value  in  body_dict .items ():
199+                 # If it's a nested dictionary, extract keys recursively 
200+                 properties_dict  =  self .extract_keys (key , value , properties_dict )
201+ 
202+         elif  isinstance (body_dict , list ) and  len (body_dict ) >  0 :
203+             first_item  =  body_dict [0 ]
204+             if  isinstance (first_item , dict ):
205+                 for  key , value  in  first_item .items ():
206+                     properties_dict  =  self .extract_keys (key , value , properties_dict )
207+ 
208+         # Create the schema object for this response 
209+         object_dict  =  {"type" : "object" , "properties" : properties_dict }
210+ 
211+         # Add the schema to OpenAPI spec if not already present 
212+         if  object_name  not  in openapi_spec ["components" ]["schemas" ]:
213+             openapi_spec ["components" ]["schemas" ][object_name ] =  object_dict 
214+ 
215+         reference  =  f"#/components/schemas/{ object_name }  
216+         return  reference , object_name , openapi_spec 
217+ 
218+     def  extract_keys (self , key : str , value : Any , properties_dict : Dict [str , Any ]) ->  Dict [str , Any ]:
219+         """ 
220+         Extracts and formats the keys and values from a dictionary to generate OpenAPI properties. 
221+ 
222+         Args: 
223+             key (str): The key in the dictionary. 
224+             value (Any): The value associated with the key. 
225+             properties_dict (Dict[str, Any]): The dictionary to store the extracted properties. 
226+ 
227+         Returns: 
228+             Dict[str, Any]: The updated properties dictionary. 
229+         """ 
230+         if  key  ==  "id" :
231+             properties_dict [key ] =  {
232+                 "type" : str (type (value ).__name__ ),
233+                 "format" : "uuid" ,
234+                 "example" : str (value ),
235+             }
236+         else :
237+             properties_dict [key ] =  {"type" : str (type (value ).__name__ ), "example" : str (value )}
238+ 
239+         return  properties_dict 
240+ 
241+ 
106242    def  update_openapi_spec (self , resp , result , prompt_engineer ):
107243        """ 
108244        Updates the OpenAPI specification based on the API response provided. 
@@ -156,7 +292,7 @@ def update_openapi_spec(self, resp, result, prompt_engineer):
156292                return  list (self .openapi_spec ["endpoints" ].keys ())
157293
158294            # Parse the response into OpenAPI example and reference 
159-             example , reference , self .openapi_spec  =  self .response_handler . parse_http_response_to_openapi_example (
295+             example , reference , self .openapi_spec  =  self .parse_http_response_to_openapi_example (
160296                self .openapi_spec , result , path , method 
161297            )
162298
0 commit comments