alita-sdk 0.3.156__py3-none-any.whl → 0.3.158__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,8 @@
1
+ import copy
1
2
  import json
2
3
  import logging
3
4
  import re
4
- from typing import Any, Dict, List, Optional
5
+ from typing import Any, Dict, List, Optional, Tuple
5
6
  from traceback import format_exc
6
7
 
7
8
  import requests
@@ -22,6 +23,16 @@ PostmanGetCollection = create_model(
22
23
  "PostmanGetCollection"
23
24
  )
24
25
 
26
+ PostmanGetCollectionFlat = create_model(
27
+ "PostmanGetCollectionFlat"
28
+ )
29
+
30
+ PostmanGetFolderFlat = create_model(
31
+ "PostmanGetFolderFlat",
32
+ folder_path=(str, Field(
33
+ description="The path to the folder to parse (e.g., 'API/Users' for nested folders)"))
34
+ )
35
+
25
36
  PostmanGetFolder = create_model(
26
37
  "PostmanGetFolder",
27
38
  folder_path=(str, Field(
@@ -44,24 +55,17 @@ PostmanSearchRequests = create_model(
44
55
  description="Optional HTTP method filter", default=None))
45
56
  )
46
57
 
47
- PostmanAnalyzeCollection = create_model(
48
- "PostmanAnalyzeCollection",
49
- include_improvements=(bool, Field(
50
- description="Include improvement suggestions in the analysis", default=False))
51
- )
52
-
53
- PostmanAnalyzeFolder = create_model(
54
- "PostmanAnalyzeFolder",
55
- folder_path=(str, Field(description="The path to the folder to analyze")),
56
- include_improvements=(bool, Field(
57
- description="Include improvement suggestions in the analysis", default=False))
58
- )
59
-
60
- PostmanAnalyzeRequest = create_model(
61
- "PostmanAnalyzeRequest",
62
- request_path=(str, Field(description="The path to the request to analyze")),
58
+ PostmanAnalyze = create_model(
59
+ "PostmanAnalyze",
60
+ scope=(str, Field(
61
+ description="The scope of analysis: 'collection', 'folder', or 'request'",
62
+ default="collection")),
63
+ target_path=(Optional[str], Field(
64
+ description="The path to the folder or request to analyze (required for folder/request scope)",
65
+ default=None)),
63
66
  include_improvements=(bool, Field(
64
- description="Include improvement suggestions in the analysis", default=False))
67
+ description="Include improvement suggestions in the analysis",
68
+ default=False))
65
69
  )
66
70
 
67
71
  PostmanCreateCollection = create_model(
@@ -234,6 +238,24 @@ PostmanMoveRequest = create_model(
234
238
  description="New folder path", default=None))
235
239
  )
236
240
 
241
+ PostmanGetRequestByPath = create_model(
242
+ "PostmanGetRequestByPath",
243
+ request_path=(str, Field(
244
+ description="The path to the request (e.g., 'API/Users/Get User' or 'applications/recommendations')"))
245
+ )
246
+
247
+ PostmanGetRequestById = create_model(
248
+ "PostmanGetRequestById",
249
+ request_id=(str, Field(
250
+ description="The unique ID of the request"))
251
+ )
252
+
253
+ PostmanGetRequestScript = create_model(
254
+ "PostmanGetRequestScript",
255
+ request_path=(str, Field(description="Path to the request (folder/requestName)")),
256
+ script_type=(str, Field(description="The type of script to retrieve: 'test' or 'prerequest'", default="prerequest"))
257
+ )
258
+
237
259
 
238
260
  class PostmanApiWrapper(BaseToolApiWrapper):
239
261
  """Wrapper for Postman API."""
@@ -283,8 +305,14 @@ class PostmanApiWrapper(BaseToolApiWrapper):
283
305
  return {}
284
306
 
285
307
  except requests.exceptions.RequestException as e:
286
- logger.error(f"Request failed: {e}")
287
- raise ToolException(f"Postman API request failed: {str(e)}")
308
+ error_details = ""
309
+ if hasattr(e, 'response') and e.response is not None:
310
+ try:
311
+ error_details = f" Response content: {e.response.text}"
312
+ except:
313
+ error_details = f" Response status: {e.response.status_code}"
314
+ logger.error(f"Request failed: {e}{error_details}")
315
+ raise ToolException(f"Postman API request failed: {str(e)}{error_details}")
288
316
  except json.JSONDecodeError as e:
289
317
  logger.error(f"Failed to decode JSON response: {e}")
290
318
  raise ToolException(
@@ -300,26 +328,61 @@ class PostmanApiWrapper(BaseToolApiWrapper):
300
328
  "args_schema": PostmanGetCollections,
301
329
  "ref": self.get_collections
302
330
  },
331
+ # {
332
+ # "name": "get_collection",
333
+ # "mode": "get_collection",
334
+ # "description": "Get a specific Postman collection by ID",
335
+ # "args_schema": PostmanGetCollection,
336
+ # "ref": self.get_collection
337
+ # },
303
338
  {
304
339
  "name": "get_collection",
305
340
  "mode": "get_collection",
306
- "description": "Get a specific Postman collection by ID",
307
- "args_schema": PostmanGetCollection,
308
- "ref": self.get_collection
341
+ "description": "Get a specific Postman collection in flattened format with path-based structure",
342
+ "args_schema": PostmanGetCollectionFlat,
343
+ "ref": self.get_collection_flat
309
344
  },
310
345
  {
311
346
  "name": "get_folder",
312
347
  "mode": "get_folder",
313
- "description": "Get folders from a collection by path (supports nested paths like 'API/Users')",
314
- "args_schema": PostmanGetFolder,
315
- "ref": self.get_folder
348
+ "description": "Get a specific folder in flattened format with path-based structure",
349
+ "args_schema": PostmanGetFolderFlat,
350
+ "ref": self.get_folder_flat
351
+ },
352
+ # {
353
+ # "name": "get_folder",
354
+ # "mode": "get_folder",
355
+ # "description": "Get folders from a collection by path (supports nested paths like 'API/Users')",
356
+ # "args_schema": PostmanGetFolder,
357
+ # "ref": self.get_folder
358
+ # },
359
+ # {
360
+ # "name": "get_folder_requests",
361
+ # "mode": "get_folder_requests",
362
+ # "description": "Get detailed information about all requests in a folder",
363
+ # "args_schema": PostmanGetFolderRequests,
364
+ # "ref": self.get_folder_requests
365
+ # },
366
+ {
367
+ "name": "get_request_by_path",
368
+ "mode": "get_request_by_path",
369
+ "description": "Get a specific request by path",
370
+ "args_schema": PostmanGetRequestByPath,
371
+ "ref": self.get_request_by_path
316
372
  },
317
373
  {
318
- "name": "get_folder_requests",
319
- "mode": "get_folder_requests",
320
- "description": "Get detailed information about all requests in a folder",
321
- "args_schema": PostmanGetFolderRequests,
322
- "ref": self.get_folder_requests
374
+ "name": "get_request_by_id",
375
+ "mode": "get_request_by_id",
376
+ "description": "Get a specific request by ID",
377
+ "args_schema": PostmanGetRequestById,
378
+ "ref": self.get_request_by_id
379
+ },
380
+ {
381
+ "name": "get_request_script",
382
+ "mode": "get_request_script",
383
+ "description": "Get the test or pre-request script content for a specific request",
384
+ "args_schema": PostmanGetRequestScript,
385
+ "ref": self.get_request_script
323
386
  },
324
387
  {
325
388
  "name": "search_requests",
@@ -329,40 +392,26 @@ class PostmanApiWrapper(BaseToolApiWrapper):
329
392
  "ref": self.search_requests
330
393
  },
331
394
  {
332
- "name": "analyze_collection",
333
- "mode": "analyze_collection",
334
- "description": "Analyze a collection for API quality, best practices, and issues",
335
- "args_schema": PostmanAnalyzeCollection,
336
- "ref": self.analyze_collection
337
- },
338
- {
339
- "name": "analyze_folder",
340
- "mode": "analyze_folder",
341
- "description": "Analyze a specific folder within a collection",
342
- "args_schema": PostmanAnalyzeFolder,
343
- "ref": self.analyze_folder
344
- },
345
- {
346
- "name": "analyze_request",
347
- "mode": "analyze_request",
348
- "description": "Analyze a specific request within a collection",
349
- "args_schema": PostmanAnalyzeRequest,
350
- "ref": self.analyze_request
351
- },
352
- {
353
- "name": "create_collection",
354
- "mode": "create_collection",
355
- "description": "Create a new Postman collection",
356
- "args_schema": PostmanCreateCollection,
357
- "ref": self.create_collection
358
- },
359
- {
360
- "name": "update_collection_name",
361
- "mode": "update_collection_name",
362
- "description": "Update collection name",
363
- "args_schema": PostmanUpdateCollectionName,
364
- "ref": self.update_collection_name
395
+ "name": "analyze",
396
+ "mode": "analyze",
397
+ "description": "Analyze collection, folder, or request for API quality, best practices, and issues",
398
+ "args_schema": PostmanAnalyze,
399
+ "ref": self.analyze
365
400
  },
401
+ # {
402
+ # "name": "create_collection",
403
+ # "mode": "create_collection",
404
+ # "description": "Create a new Postman collection",
405
+ # "args_schema": PostmanCreateCollection,
406
+ # "ref": self.create_collection
407
+ # },
408
+ # {
409
+ # "name": "update_collection_name",
410
+ # "mode": "update_collection_name",
411
+ # "description": "Update collection name",
412
+ # "args_schema": PostmanUpdateCollectionName,
413
+ # "ref": self.update_collection_name
414
+ # },
366
415
  {
367
416
  "name": "update_collection_description",
368
417
  "mode": "update_collection_description",
@@ -537,8 +586,7 @@ class PostmanApiWrapper(BaseToolApiWrapper):
537
586
  def get_collection(self, **kwargs) -> str:
538
587
  """Get a specific collection by ID."""
539
588
  try:
540
- response = self._make_request(
541
- 'GET', f'/collections/{self.collection_id}')
589
+ response = self._make_request('GET', f'/collections/{self.collection_id}')
542
590
  return json.dumps(response, indent=2)
543
591
  except Exception as e:
544
592
  stacktrace = format_exc()
@@ -547,6 +595,19 @@ class PostmanApiWrapper(BaseToolApiWrapper):
547
595
  raise ToolException(
548
596
  f"Unable to get collection {self.collection_id}: {str(e)}")
549
597
 
598
+ def get_collection_flat(self, **kwargs) -> str:
599
+ """Get a specific collection by ID in flattened format."""
600
+ try:
601
+ response = self._make_request('GET', f'/collections/{self.collection_id}')
602
+ flattened = self.parse_collection_to_flat_structure(response)
603
+ return json.dumps(flattened, indent=2)
604
+ except Exception as e:
605
+ stacktrace = format_exc()
606
+ logger.error(
607
+ f"Exception when getting flattened collection {self.collection_id}: {stacktrace}")
608
+ raise ToolException(
609
+ f"Unable to get flattened collection {self.collection_id}: {str(e)}")
610
+
550
611
  def get_folder(self, folder_path: str, **kwargs) -> str:
551
612
  """Get folders from a collection by path."""
552
613
  try:
@@ -562,6 +623,19 @@ class PostmanApiWrapper(BaseToolApiWrapper):
562
623
  raise ToolException(
563
624
  f"Unable to get folder {folder_path} from collection {self.collection_id}: {str(e)}")
564
625
 
626
+ def get_folder_flat(self, folder_path: str, **kwargs) -> str:
627
+ """Get a specific folder in flattened format with path-based structure."""
628
+ try:
629
+ response = self._make_request('GET', f'/collections/{self.collection_id}')
630
+ flattened = self.parse_collection_to_flat_structure(response, folder_path)
631
+ return json.dumps(flattened, indent=2)
632
+ except Exception as e:
633
+ stacktrace = format_exc()
634
+ logger.error(
635
+ f"Exception when getting flattened folder {folder_path}: {stacktrace}")
636
+ raise ToolException(
637
+ f"Unable to get flattened folder {folder_path}: {str(e)}")
638
+
565
639
  def get_folder_requests(self, folder_path: str, include_details: bool = False, **kwargs) -> str:
566
640
  """Get detailed information about all requests in a folder."""
567
641
  try:
@@ -593,20 +667,55 @@ class PostmanApiWrapper(BaseToolApiWrapper):
593
667
  f"Unable to get requests from folder {folder_path}: {str(e)}")
594
668
 
595
669
  def search_requests(self, query: str, search_in: str = "all", method: str = None, **kwargs) -> str:
596
- """Search for requests across the collection."""
670
+ """Search for requests across the collection and return results in flattened structure."""
597
671
  try:
598
- collection = self._make_request(
672
+ collection_response = self._make_request(
599
673
  'GET', f'/collections/{self.collection_id}')
600
- requests = self.analyzer.search_requests_in_items(
601
- collection['collection']['item'], query, search_in, method)
602
-
674
+
675
+ # Get the collection in flattened structure
676
+ flattened = self.parse_collection_to_flat_structure(collection_response)
677
+
678
+ # Filter only requests that match the search criteria
679
+ matching_requests = {}
680
+
681
+ for path, item in flattened['items'].items():
682
+ if item.get('type') != 'request':
683
+ continue
684
+
685
+ # Apply method filter if specified
686
+ if method and item.get('method', '').upper() != method.upper():
687
+ continue
688
+
689
+ # Apply search criteria
690
+ match_found = False
691
+ query_lower = query.lower()
692
+
693
+ if search_in == "all" or search_in == "name":
694
+ # Extract request name from path (last part after /)
695
+ request_name = path.split('/')[-1] if '/' in path else path
696
+ if query_lower in request_name.lower():
697
+ match_found = True
698
+
699
+ if not match_found and (search_in == "all" or search_in == "url"):
700
+ url = item.get('request_url', '') or item.get('url', '')
701
+ if query_lower in url.lower():
702
+ match_found = True
703
+
704
+ if not match_found and (search_in == "all" or search_in == "description"):
705
+ description = item.get('description', '')
706
+ if isinstance(description, str) and query_lower in description.lower():
707
+ match_found = True
708
+
709
+ if match_found:
710
+ matching_requests[path] = item
711
+
712
+ # Create result structure similar to flattened format
603
713
  result = {
604
- "collection_id": self.collection_id,
605
714
  "query": query,
606
715
  "search_in": search_in,
607
716
  "method_filter": method,
608
- "results_count": len(requests),
609
- "results": requests
717
+ "results_count": len(matching_requests),
718
+ "items": matching_requests
610
719
  }
611
720
 
612
721
  return json.dumps(result, indent=2)
@@ -616,83 +725,85 @@ class PostmanApiWrapper(BaseToolApiWrapper):
616
725
  raise ToolException(
617
726
  f"Unable to search requests in collection {self.collection_id}: {str(e)}")
618
727
 
619
- def analyze_collection(self, include_improvements: bool = False, **kwargs) -> str:
620
- """Analyze a collection for API quality and best practices."""
728
+ def analyze(self, scope: str = "collection", target_path: str = None, include_improvements: bool = False, **kwargs) -> str:
729
+ """Unified analysis method for collection, folder, or request analysis.
730
+
731
+ Args:
732
+ scope: The scope of analysis ('collection', 'folder', or 'request')
733
+ target_path: The path to the folder or request (required for folder/request scope)
734
+ include_improvements: Whether to include improvement suggestions
735
+ """
621
736
  try:
622
- collection = self._make_request(
623
- 'GET', f'/collections/{self.collection_id}')
624
- analysis = self.analyzer.perform_collection_analysis(collection)
737
+ # Validate parameters
738
+ if scope not in ["collection", "folder", "request"]:
739
+ raise ToolException(f"Invalid scope '{scope}'. Must be 'collection', 'folder', or 'request'")
625
740
 
626
- if include_improvements:
627
- improvements = self.analyzer.generate_improvements(analysis)
628
- analysis["improvements"] = improvements
629
- analysis["improvement_count"] = len(improvements)
741
+ if scope in ["folder", "request"] and not target_path:
742
+ raise ToolException(f"target_path is required when scope is '{scope}'")
630
743
 
631
- return json.dumps(analysis, indent=2)
632
- except Exception as e:
633
- stacktrace = format_exc()
634
- logger.error(f"Exception when analyzing collection: {stacktrace}")
635
- raise ToolException(
636
- f"Unable to analyze collection {self.collection_id}: {str(e)}")
637
-
638
- def analyze_folder(self, folder_path: str, include_improvements: bool = False, **kwargs) -> str:
639
- """Analyze a specific folder within a collection."""
640
- try:
744
+ # Get collection data
641
745
  collection = self._make_request(
642
746
  'GET', f'/collections/{self.collection_id}')
643
- folders = self.analyzer.find_folders_by_path(
644
- collection['collection']['item'], folder_path)
645
-
646
- if not folders:
647
- return json.dumps({"error": f"Folder '{folder_path}' not found"}, indent=2)
648
-
649
- folder_analyses = []
650
- for folder in folders:
651
- analysis = self.analyzer.perform_folder_analysis(folder, folder_path)
747
+
748
+ if scope == "collection":
749
+ # Analyze entire collection
750
+ analysis = self.analyzer.perform_collection_analysis(collection)
652
751
 
653
752
  if include_improvements:
654
- improvements = self.analyzer.generate_folder_improvements(analysis)
753
+ improvements = self.analyzer.generate_improvements(analysis)
655
754
  analysis["improvements"] = improvements
656
755
  analysis["improvement_count"] = len(improvements)
657
756
 
658
- folder_analyses.append(analysis)
659
-
660
- return json.dumps(folder_analyses, indent=2)
661
- except Exception as e:
662
- stacktrace = format_exc()
663
- logger.error(f"Exception when analyzing folder: {stacktrace}")
664
- raise ToolException(
665
- f"Unable to analyze folder {folder_path}: {str(e)}")
757
+ return json.dumps(analysis, indent=2)
758
+
759
+ elif scope == "folder":
760
+ # Analyze specific folder
761
+ folders = self.analyzer.find_folders_by_path(
762
+ collection['collection']['item'], target_path)
666
763
 
667
- def analyze_request(self, request_path: str, include_improvements: bool = False, **kwargs) -> str:
668
- """Analyze a specific request within a collection."""
669
- try:
670
- collection = self._make_request(
671
- 'GET', f'/collections/{self.collection_id}')
672
- collection_data = collection["collection"]
764
+ if not folders:
765
+ return json.dumps({"error": f"Folder '{target_path}' not found"}, indent=2)
766
+
767
+ folder_analyses = []
768
+ for folder in folders:
769
+ analysis = self.analyzer.perform_folder_analysis(folder, target_path)
770
+
771
+ if include_improvements:
772
+ improvements = self.analyzer.generate_folder_improvements(analysis)
773
+ analysis["improvements"] = improvements
774
+ analysis["improvement_count"] = len(improvements)
775
+
776
+ folder_analyses.append(analysis)
777
+
778
+ return json.dumps(folder_analyses, indent=2)
779
+
780
+ elif scope == "request":
781
+ # Analyze specific request
782
+ collection_data = collection["collection"]
673
783
 
674
- # Find the request
675
- request_item = self.analyzer.find_request_by_path(
676
- collection_data["item"], request_path)
677
- if not request_item:
678
- raise ToolException(f"Request '{request_path}' not found")
784
+ # Find the request
785
+ request_item = self.analyzer.find_request_by_path(
786
+ collection_data["item"], target_path)
787
+ if not request_item:
788
+ raise ToolException(f"Request '{target_path}' not found")
679
789
 
680
- # Perform request analysis
681
- analysis = self.analyzer.perform_request_analysis(request_item)
682
- analysis["request_path"] = request_path
683
- analysis["collection_id"] = self.collection_id
684
-
685
- if include_improvements:
686
- improvements = self.analyzer.generate_request_improvements(analysis)
687
- analysis["improvements"] = improvements
688
- analysis["improvement_count"] = len(improvements)
790
+ # Perform request analysis
791
+ analysis = self.analyzer.perform_request_analysis(request_item)
792
+ analysis["request_path"] = target_path
793
+ analysis["collection_id"] = self.collection_id
794
+
795
+ if include_improvements:
796
+ improvements = self.analyzer.generate_request_improvements(analysis)
797
+ analysis["improvements"] = improvements
798
+ analysis["improvement_count"] = len(improvements)
689
799
 
690
- return json.dumps(analysis, indent=2)
800
+ return json.dumps(analysis, indent=2)
801
+
691
802
  except Exception as e:
692
803
  stacktrace = format_exc()
693
- logger.error(f"Exception when analyzing request: {stacktrace}")
804
+ logger.error(f"Exception when analyzing {scope}: {stacktrace}")
694
805
  raise ToolException(
695
- f"Unable to analyze request {request_path}: {str(e)}")
806
+ f"Unable to analyze {scope} {target_path or self.collection_id}: {str(e)}")
696
807
 
697
808
  # =================================================================
698
809
  # COLLECTION MANAGEMENT METHODS
@@ -891,35 +1002,60 @@ class PostmanApiWrapper(BaseToolApiWrapper):
891
1002
  logger.error(f"Exception when creating folder: {stacktrace}")
892
1003
  raise ToolException(f"Unable to create folder '{name}': {str(e)}")
893
1004
 
1005
+ def _get_folder_id(self, folder_path: str) -> str:
1006
+ """Helper method to get folder ID by path."""
1007
+ collection = self._make_request(
1008
+ 'GET', f'/collections/{self.collection_id}')
1009
+ collection_data = collection["collection"]
1010
+
1011
+ # Find the folder
1012
+ folders = self.analyzer.find_folders_by_path(
1013
+ collection_data["item"], folder_path)
1014
+ if not folders:
1015
+ raise ToolException(f"Folder '{folder_path}' not found")
1016
+
1017
+ folder = folders[0]
1018
+
1019
+ # Get the folder ID
1020
+ folder_id = folder.get("id")
1021
+ if not folder_id:
1022
+ # If ID is not available directly, try to use the item ID
1023
+ if "_postman_id" in folder:
1024
+ folder_id = folder["_postman_id"]
1025
+ else:
1026
+ raise ToolException(f"Folder ID not found for '{folder_path}'")
1027
+
1028
+ return folder_id
1029
+
894
1030
  def update_folder(self, folder_path: str, name: str = None,
895
1031
  description: str = None, auth: Dict = None, **kwargs) -> str:
896
- """Update folder properties."""
1032
+ """Update folder properties using the direct folder endpoint."""
897
1033
  try:
898
- # Get current collection
899
- collection = self._make_request(
900
- 'GET', f'/collections/{self.collection_id}')
901
- collection_data = collection["collection"]
902
-
903
- # Find the folder
904
- folders = self.analyzer.find_folders_by_path(
905
- collection_data["item"], folder_path)
906
- if not folders:
907
- raise ToolException(f"Folder '{folder_path}' not found")
908
-
909
- folder = folders[0]
910
-
911
- # Update fields if provided
1034
+ # Get the folder ID
1035
+ folder_id = self._get_folder_id(folder_path)
1036
+
1037
+ # Create update payload
1038
+ folder_update = {}
912
1039
  if name:
913
- folder["name"] = name
1040
+ folder_update["name"] = name
914
1041
  if description is not None:
915
- folder["description"] = description
1042
+ folder_update["description"] = description
916
1043
  if auth is not None:
917
- folder["auth"] = auth
918
-
919
- # Update collection
920
- response = self._make_request('PUT', f'/collections/{self.collection_id}',
921
- json={"collection": collection_data})
922
- return json.dumps({"message": f"Folder '{folder_path}' updated successfully"}, indent=2)
1044
+ folder_update["auth"] = auth
1045
+
1046
+ # Only update if we have properties to change
1047
+ if folder_update:
1048
+ # Update folder using the direct API endpoint
1049
+ response = self._make_request('PUT', f'/collections/{self.collection_id}/folders/{folder_id}',
1050
+ json=folder_update)
1051
+ return json.dumps({"success": True, "message": f"Folder '{folder_path}' updated successfully"}, indent=2)
1052
+ else:
1053
+ return json.dumps({"success": True, "message": f"No changes requested for folder '{folder_path}'"}, indent=2)
1054
+ except Exception as e:
1055
+ stacktrace = format_exc()
1056
+ logger.error(f"Exception when updating folder: {stacktrace}")
1057
+ raise ToolException(
1058
+ f"Unable to update folder '{folder_path}': {str(e)}")
923
1059
  except Exception as e:
924
1060
  stacktrace = format_exc()
925
1061
  logger.error(f"Exception when updating folder: {stacktrace}")
@@ -1063,24 +1199,18 @@ class PostmanApiWrapper(BaseToolApiWrapper):
1063
1199
  def update_request_name(self, request_path: str, name: str, **kwargs) -> str:
1064
1200
  """Update request name."""
1065
1201
  try:
1066
- # Get current collection
1067
- collection = self._make_request(
1068
- 'GET', f'/collections/{self.collection_id}')
1069
- collection_data = collection["collection"]
1070
-
1071
- # Find the request
1072
- request_item = self.analyzer.find_request_by_path(
1073
- collection_data["item"], request_path)
1074
- if not request_item:
1075
- raise ToolException(f"Request '{request_path}' not found")
1076
-
1077
- # Update name
1078
- request_item["name"] = name
1202
+ # Get request item and ID
1203
+ request_item, request_id, _ = self._get_request_item_and_id(request_path)
1204
+
1205
+ # Create update payload
1206
+ request_update = {
1207
+ "name": name
1208
+ }
1079
1209
 
1080
- # Update collection
1081
- response = self._make_request('PUT', f'/collections/{self.collection_id}',
1082
- json={"collection": collection_data})
1083
- return json.dumps({"message": f"Request '{request_path}' name updated successfully"}, indent=2)
1210
+ # Update the name field
1211
+ response = self._make_request('PUT', f'/collections/{self.collection_id}/requests/{request_id}',
1212
+ json=request_update)
1213
+ return json.dumps({"success": True, "message": f"Request '{request_path}' name updated successfully"}, indent=2)
1084
1214
  except Exception as e:
1085
1215
  stacktrace = format_exc()
1086
1216
  logger.error(f"Exception when updating request name: {stacktrace}")
@@ -1090,24 +1220,18 @@ class PostmanApiWrapper(BaseToolApiWrapper):
1090
1220
  def update_request_method(self, request_path: str, method: str, **kwargs) -> str:
1091
1221
  """Update request HTTP method."""
1092
1222
  try:
1093
- # Get current collection
1094
- collection = self._make_request(
1095
- 'GET', f'/collections/{self.collection_id}')
1096
- collection_data = collection["collection"]
1097
-
1098
- # Find the request
1099
- request_item = self.analyzer.find_request_by_path(
1100
- collection_data["item"], request_path)
1101
- if not request_item:
1102
- raise ToolException(f"Request '{request_path}' not found")
1103
-
1104
- # Update method
1105
- request_item["request"]["method"] = method.upper()
1223
+ # Get request item and ID
1224
+ request_item, request_id, _ = self._get_request_item_and_id(request_path)
1225
+
1226
+ # Create update payload
1227
+ request_update = {
1228
+ "method": method.upper()
1229
+ }
1106
1230
 
1107
- # Update collection
1108
- response = self._make_request('PUT', f'/collections/{self.collection_id}',
1109
- json={"collection": collection_data})
1110
- return json.dumps({"message": f"Request '{request_path}' method updated successfully"}, indent=2)
1231
+ # Update the method field
1232
+ response = self._make_request('PUT', f'/collections/{self.collection_id}/requests/{request_id}',
1233
+ json=request_update)
1234
+ return json.dumps({"success": True, "message": f"Request '{request_path}' method updated successfully"}, indent=2)
1111
1235
  except Exception as e:
1112
1236
  stacktrace = format_exc()
1113
1237
  logger.error(f"Exception when updating request method: {stacktrace}")
@@ -1117,51 +1241,63 @@ class PostmanApiWrapper(BaseToolApiWrapper):
1117
1241
  def update_request_url(self, request_path: str, url: str, **kwargs) -> str:
1118
1242
  """Update request URL."""
1119
1243
  try:
1120
- # Get current collection
1121
- collection = self._make_request(
1122
- 'GET', f'/collections/{self.collection_id}')
1123
- collection_data = collection["collection"]
1124
-
1125
- # Find the request
1126
- request_item = self.analyzer.find_request_by_path(
1127
- collection_data["item"], request_path)
1128
- if not request_item:
1129
- raise ToolException(f"Request '{request_path}' not found")
1130
-
1131
- # Update URL
1132
- request_item["request"]["url"] = url
1244
+ # Get request item and ID
1245
+ request_item, request_id, _ = self._get_request_item_and_id(request_path)
1246
+
1247
+ # Create update payload
1248
+ request_update = {
1249
+ "url": url
1250
+ }
1133
1251
 
1134
- # Update collection
1135
- response = self._make_request('PUT', f'/collections/{self.collection_id}',
1136
- json={"collection": collection_data})
1137
- return json.dumps({"message": f"Request '{request_path}' URL updated successfully"}, indent=2)
1252
+ # Update the URL field
1253
+ response = self._make_request('PUT', f'/collections/{self.collection_id}/requests/{request_id}',
1254
+ json=request_update)
1255
+ return json.dumps({"success": True, "message": f"Request '{request_path}' URL updated successfully"}, indent=2)
1138
1256
  except Exception as e:
1139
1257
  stacktrace = format_exc()
1140
1258
  logger.error(f"Exception when updating request URL: {stacktrace}")
1141
1259
  raise ToolException(
1142
1260
  f"Unable to update request '{request_path}' URL: {str(e)}")
1143
1261
 
1262
+ def _get_request_item_and_id(self, request_path: str) -> Tuple[Dict, str, Dict]:
1263
+ """Helper method to get request item and ID by path. Returns (request_item, request_id, collection_data)."""
1264
+ collection = self._make_request(
1265
+ 'GET', f'/collections/{self.collection_id}')
1266
+ collection_data = collection["collection"]
1267
+
1268
+ # Find the request
1269
+ request_item = self.analyzer.find_request_by_path(
1270
+ collection_data["item"], request_path)
1271
+ if not request_item:
1272
+ raise ToolException(f"Request '{request_path}' not found")
1273
+
1274
+ # Get the request ID
1275
+ request_id = request_item.get("id")
1276
+ if not request_id:
1277
+ # If ID is not available directly, try to use the full item ID path
1278
+ if "_postman_id" in request_item:
1279
+ request_id = request_item["_postman_id"]
1280
+ else:
1281
+ raise ToolException(f"Request ID not found for '{request_path}'")
1282
+
1283
+ return request_item, request_id, collection_data
1284
+
1144
1285
  def update_request_description(self, request_path: str, description: str, **kwargs) -> str:
1145
1286
  """Update request description."""
1146
1287
  try:
1147
- # Get current collection
1148
- collection = self._make_request(
1149
- 'GET', f'/collections/{self.collection_id}')
1150
- collection_data = collection["collection"]
1151
-
1152
- # Find the request
1153
- request_item = self.analyzer.find_request_by_path(
1154
- collection_data["item"], request_path)
1155
- if not request_item:
1156
- raise ToolException(f"Request '{request_path}' not found")
1157
-
1158
- # Update description
1159
- request_item["request"]["description"] = description
1288
+ # Get request item and ID
1289
+ request_item, request_id, _ = self._get_request_item_and_id(request_path)
1290
+
1291
+ # For description update, we need to properly format the payload
1292
+ # according to Postman API requirements
1293
+ request_update = {
1294
+ "description": description
1295
+ }
1160
1296
 
1161
- # Update collection
1162
- response = self._make_request('PUT', f'/collections/{self.collection_id}',
1163
- json={"collection": collection_data})
1164
- return json.dumps({"message": f"Request '{request_path}' description updated successfully"}, indent=2)
1297
+ # Update only the description field
1298
+ response = self._make_request('PUT', f'/collections/{self.collection_id}/requests/{request_id}',
1299
+ json=request_update)
1300
+ return json.dumps({"success": True, "message": f"Request '{request_path}' description updated successfully"}, indent=2)
1165
1301
  except Exception as e:
1166
1302
  stacktrace = format_exc()
1167
1303
  logger.error(f"Exception when updating request description: {stacktrace}")
@@ -1171,24 +1307,18 @@ class PostmanApiWrapper(BaseToolApiWrapper):
1171
1307
  def update_request_headers(self, request_path: str, headers: List[Dict], **kwargs) -> str:
1172
1308
  """Update request headers."""
1173
1309
  try:
1174
- # Get current collection
1175
- collection = self._make_request(
1176
- 'GET', f'/collections/{self.collection_id}')
1177
- collection_data = collection["collection"]
1178
-
1179
- # Find the request
1180
- request_item = self.analyzer.find_request_by_path(
1181
- collection_data["item"], request_path)
1182
- if not request_item:
1183
- raise ToolException(f"Request '{request_path}' not found")
1184
-
1185
- # Update headers
1186
- request_item["request"]["header"] = headers
1310
+ # Get request item and ID
1311
+ request_item, request_id, _ = self._get_request_item_and_id(request_path)
1312
+
1313
+ # Create update payload
1314
+ request_update = {
1315
+ "header": headers
1316
+ }
1187
1317
 
1188
- # Update collection
1189
- response = self._make_request('PUT', f'/collections/{self.collection_id}',
1190
- json={"collection": collection_data})
1191
- return json.dumps({"message": f"Request '{request_path}' headers updated successfully"}, indent=2)
1318
+ # Update the headers field
1319
+ response = self._make_request('PUT', f'/collections/{self.collection_id}/requests/{request_id}',
1320
+ json=request_update)
1321
+ return json.dumps({"success": True, "message": f"Request '{request_path}' headers updated successfully"}, indent=2)
1192
1322
  except Exception as e:
1193
1323
  stacktrace = format_exc()
1194
1324
  logger.error(f"Exception when updating request headers: {stacktrace}")
@@ -1198,24 +1328,18 @@ class PostmanApiWrapper(BaseToolApiWrapper):
1198
1328
  def update_request_body(self, request_path: str, body: Dict, **kwargs) -> str:
1199
1329
  """Update request body."""
1200
1330
  try:
1201
- # Get current collection
1202
- collection = self._make_request(
1203
- 'GET', f'/collections/{self.collection_id}')
1204
- collection_data = collection["collection"]
1205
-
1206
- # Find the request
1207
- request_item = self.analyzer.find_request_by_path(
1208
- collection_data["item"], request_path)
1209
- if not request_item:
1210
- raise ToolException(f"Request '{request_path}' not found")
1211
-
1212
- # Update body
1213
- request_item["request"]["body"] = body
1331
+ # Get request item and ID
1332
+ request_item, request_id, _ = self._get_request_item_and_id(request_path)
1333
+
1334
+ # Create update payload
1335
+ request_update = {
1336
+ "body": body
1337
+ }
1214
1338
 
1215
- # Update collection
1216
- response = self._make_request('PUT', f'/collections/{self.collection_id}',
1217
- json={"collection": collection_data})
1218
- return json.dumps({"message": f"Request '{request_path}' body updated successfully"}, indent=2)
1339
+ # Update the body field
1340
+ response = self._make_request('PUT', f'/collections/{self.collection_id}/requests/{request_id}',
1341
+ json=request_update)
1342
+ return json.dumps({"success": True, "message": f"Request '{request_path}' body updated successfully"}, indent=2)
1219
1343
  except Exception as e:
1220
1344
  stacktrace = format_exc()
1221
1345
  logger.error(f"Exception when updating request body: {stacktrace}")
@@ -1225,24 +1349,18 @@ class PostmanApiWrapper(BaseToolApiWrapper):
1225
1349
  def update_request_auth(self, request_path: str, auth: Dict, **kwargs) -> str:
1226
1350
  """Update request authentication."""
1227
1351
  try:
1228
- # Get current collection
1229
- collection = self._make_request(
1230
- 'GET', f'/collections/{self.collection_id}')
1231
- collection_data = collection["collection"]
1232
-
1233
- # Find the request
1234
- request_item = self.analyzer.find_request_by_path(
1235
- collection_data["item"], request_path)
1236
- if not request_item:
1237
- raise ToolException(f"Request '{request_path}' not found")
1238
-
1239
- # Update auth
1240
- request_item["request"]["auth"] = auth
1352
+ # Get request item and ID
1353
+ request_item, request_id, _ = self._get_request_item_and_id(request_path)
1354
+
1355
+ # Create update payload
1356
+ request_update = {
1357
+ "auth": auth
1358
+ }
1241
1359
 
1242
- # Update collection
1243
- response = self._make_request('PUT', f'/collections/{self.collection_id}',
1244
- json={"collection": collection_data})
1245
- return json.dumps({"message": f"Request '{request_path}' auth updated successfully"}, indent=2)
1360
+ # Update the auth field
1361
+ response = self._make_request('PUT', f'/collections/{self.collection_id}/requests/{request_id}',
1362
+ json=request_update)
1363
+ return json.dumps({"success": True, "message": f"Request '{request_path}' auth updated successfully"}, indent=2)
1246
1364
  except Exception as e:
1247
1365
  stacktrace = format_exc()
1248
1366
  logger.error(f"Exception when updating request auth: {stacktrace}")
@@ -1252,35 +1370,34 @@ class PostmanApiWrapper(BaseToolApiWrapper):
1252
1370
  def update_request_tests(self, request_path: str, tests: str, **kwargs) -> str:
1253
1371
  """Update request test scripts."""
1254
1372
  try:
1255
- # Get current collection
1256
- collection = self._make_request(
1257
- 'GET', f'/collections/{self.collection_id}')
1258
- collection_data = collection["collection"]
1259
-
1260
- # Find the request
1261
- request_item = self.analyzer.find_request_by_path(
1262
- collection_data["item"], request_path)
1263
- if not request_item:
1264
- raise ToolException(f"Request '{request_path}' not found")
1265
-
1266
- # Update test events
1267
- events = request_item.get("event", [])
1268
- # Remove existing test events
1269
- events = [e for e in events if e.get("listen") != "test"]
1270
- if tests:
1271
- events.append({
1272
- "listen": "test",
1273
- "script": {
1274
- "exec": tests.split('\n'),
1275
- "type": "text/javascript"
1276
- }
1277
- })
1278
- request_item["event"] = events
1279
-
1280
- # Update collection
1281
- response = self._make_request('PUT', f'/collections/{self.collection_id}',
1282
- json={"collection": collection_data})
1283
- return json.dumps({"message": f"Request '{request_path}' tests updated successfully"}, indent=2)
1373
+ # Get the request ID
1374
+ _, request_id, _ = self._get_request_item_and_id(request_path)
1375
+
1376
+ # Get current request to preserve existing data
1377
+ current_request = self._make_request('GET', f'/collections/{self.collection_id}/requests/{request_id}')
1378
+ request_data = current_request.get("data", {})
1379
+
1380
+ # Prepare the events array - preserve any non-test events
1381
+ events = [event for event in request_data.get("events", []) if event.get("listen") != "test"]
1382
+
1383
+ # Add the new test script
1384
+ events.append({
1385
+ "listen": "test",
1386
+ "script": {
1387
+ "type": "text/javascript",
1388
+ "exec": tests.strip().split('\n')
1389
+ }
1390
+ })
1391
+
1392
+ # Update the events array in the request data
1393
+ request_data["events"] = events
1394
+
1395
+ # Update the request using the individual request endpoint
1396
+ response = self._make_request('PUT', f'/collections/{self.collection_id}/requests/{request_id}',
1397
+ json=request_data)
1398
+
1399
+ logger.info(f"Test script updated successfully for request '{request_path}'")
1400
+ return json.dumps({"success": True, "message": f"Request '{request_path}' tests updated successfully"}, indent=2)
1284
1401
  except Exception as e:
1285
1402
  stacktrace = format_exc()
1286
1403
  logger.error(f"Exception when updating request tests: {stacktrace}")
@@ -1290,35 +1407,34 @@ class PostmanApiWrapper(BaseToolApiWrapper):
1290
1407
  def update_request_pre_script(self, request_path: str, pre_request_script: str, **kwargs) -> str:
1291
1408
  """Update request pre-request scripts."""
1292
1409
  try:
1293
- # Get current collection
1294
- collection = self._make_request(
1295
- 'GET', f'/collections/{self.collection_id}')
1296
- collection_data = collection["collection"]
1297
-
1298
- # Find the request
1299
- request_item = self.analyzer.find_request_by_path(
1300
- collection_data["item"], request_path)
1301
- if not request_item:
1302
- raise ToolException(f"Request '{request_path}' not found")
1303
-
1304
- # Update prerequest events
1305
- events = request_item.get("event", [])
1306
- # Remove existing prerequest events
1307
- events = [e for e in events if e.get("listen") != "prerequest"]
1308
- if pre_request_script:
1309
- events.append({
1310
- "listen": "prerequest",
1311
- "script": {
1312
- "exec": pre_request_script.split('\n'),
1313
- "type": "text/javascript"
1314
- }
1315
- })
1316
- request_item["event"] = events
1317
-
1318
- # Update collection
1319
- response = self._make_request('PUT', f'/collections/{self.collection_id}',
1320
- json={"collection": collection_data})
1321
- return json.dumps({"message": f"Request '{request_path}' pre-script updated successfully"}, indent=2)
1410
+ # Get the request ID
1411
+ _, request_id, _ = self._get_request_item_and_id(request_path)
1412
+
1413
+ # Get current request to preserve existing data
1414
+ current_request = self._make_request('GET', f'/collections/{self.collection_id}/requests/{request_id}')
1415
+ request_data = current_request.get("data", {})
1416
+
1417
+ # Prepare the events array - preserve any non-prerequest events
1418
+ events = [event for event in request_data.get("events", []) if event.get("listen") != "prerequest"]
1419
+
1420
+ # Add the new prerequest script
1421
+ events.append({
1422
+ "listen": "prerequest",
1423
+ "script": {
1424
+ "type": "text/javascript",
1425
+ "exec": pre_request_script.strip().split('\n')
1426
+ }
1427
+ })
1428
+
1429
+ # Update the events array in the request data
1430
+ request_data["events"] = events
1431
+
1432
+ # Update the request using the individual request endpoint
1433
+ response = self._make_request('PUT', f'/collections/{self.collection_id}/requests/{request_id}',
1434
+ json=request_data)
1435
+
1436
+ logger.info(f"Pre-request script updated successfully for request '{request_path}'")
1437
+ return json.dumps({"success": True, "message": f"Request '{request_path}' pre-script updated successfully"}, indent=2)
1322
1438
  except Exception as e:
1323
1439
  stacktrace = format_exc()
1324
1440
  logger.error(f"Exception when updating request pre-script: {stacktrace}")
@@ -1444,41 +1560,228 @@ class PostmanApiWrapper(BaseToolApiWrapper):
1444
1560
  # HELPER METHODS
1445
1561
  # =================================================================
1446
1562
 
1447
-
1448
-
1449
- def _make_request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
1450
- """Make HTTP request to Postman API."""
1451
- url = f"{self.base_url.rstrip('/')}{endpoint}"
1452
-
1563
+ def get_request_by_path(self, request_path: str, **kwargs) -> str:
1564
+ """Get a specific request by path.
1565
+
1566
+ Uses the _get_request_item_and_id helper to find the request and then fetches complete
1567
+ information using the Postman API endpoint for individual requests.
1568
+ """
1453
1569
  try:
1454
- logger.info(f"Making {method.upper()} request to {url}")
1455
- response = self.session.request(method, url, timeout=self.timeout, **kwargs)
1456
- response.raise_for_status()
1457
-
1458
- if response.content:
1459
- return response.json()
1460
- return {}
1461
-
1462
- except requests.exceptions.RequestException as e:
1463
- logger.error(f"Request failed: {e}")
1464
- raise ToolException(f"Postman API request failed: {str(e)}")
1465
- except json.JSONDecodeError as e:
1466
- logger.error(f"Failed to decode JSON response: {e}")
1570
+ # Get request item and ID
1571
+ _, request_id, _ = self._get_request_item_and_id(request_path)
1572
+
1573
+ # Fetch the complete request information from the API
1574
+ response = self._make_request(
1575
+ 'GET', f'/collections/{self.collection_id}/requests/{request_id}'
1576
+ )
1577
+
1578
+ return json.dumps(response, indent=2)
1579
+ except Exception as e:
1580
+ stacktrace = format_exc()
1581
+ logger.error(f"Exception when getting request by path: {stacktrace}")
1467
1582
  raise ToolException(
1468
- f"Invalid JSON response from Postman API: {str(e)}")
1469
-
1470
-
1471
-
1472
-
1473
-
1474
- # =================================================================
1475
- # HELPER METHODS
1476
- # =================================================================
1477
-
1478
- for item in items:
1479
- if item.get('request'): # This is a request
1480
- analysis = self.analyzer.perform_request_analysis(item)
1481
- requests.append(analysis)
1482
-
1483
- return requests
1583
+ f"Unable to get request '{request_path}': {str(e)}")
1484
1584
 
1585
+ def get_request_by_id(self, request_id: str, **kwargs) -> str:
1586
+ """Get a specific request by ID.
1587
+
1588
+ Directly fetches the request using its unique ID from the Postman API.
1589
+ """
1590
+ try:
1591
+ # Fetch the complete request information from the API using the ID
1592
+ response = self._make_request(
1593
+ 'GET', f'/collections/{self.collection_id}/requests/{request_id}'
1594
+ )
1595
+
1596
+ return json.dumps(response, indent=2)
1597
+ except Exception as e:
1598
+ stacktrace = format_exc()
1599
+ logger.error(f"Exception when getting request by ID: {stacktrace}")
1600
+ raise ToolException(
1601
+ f"Unable to get request with ID '{request_id}': {str(e)}")
1602
+
1603
+ def get_request_script(self, request_path: str, script_type: str = "prerequest", **kwargs) -> str:
1604
+ """
1605
+ Get the script (pre-request or test) for a request by path.
1606
+
1607
+ Args:
1608
+ request_path: Path to the request within the collection
1609
+ script_type: The type of script to retrieve ("prerequest" or "test")
1610
+
1611
+ Returns:
1612
+ The script content as JSON string, or an error message if the script doesn't exist
1613
+ """
1614
+ try:
1615
+ # Get the request ID and fetch current request data
1616
+ _, request_id, _ = self._get_request_item_and_id(request_path)
1617
+
1618
+ # Get current request to have the latest version with updated scripts
1619
+ current_request = self._make_request('GET', f'/collections/{self.collection_id}/requests/{request_id}')
1620
+ request_data = current_request.get("data", {})
1621
+
1622
+ # Find the script by type
1623
+ script_content = None
1624
+ for event in request_data.get("events", []):
1625
+ if event.get("listen") == script_type:
1626
+ script = event.get("script", {})
1627
+ exec_content = script.get("exec", [])
1628
+ if isinstance(exec_content, list):
1629
+ script_content = "\n".join(exec_content)
1630
+ else:
1631
+ script_content = str(exec_content)
1632
+ break
1633
+
1634
+ if script_content is None:
1635
+ return json.dumps({"success": False, "message": f"No {script_type} script found for request '{request_path}'"}, indent=2)
1636
+
1637
+ return json.dumps({
1638
+ "success": True,
1639
+ "script_type": script_type,
1640
+ "script_content": script_content,
1641
+ "request_path": request_path
1642
+ }, indent=2)
1643
+
1644
+ except Exception as e:
1645
+ stacktrace = format_exc()
1646
+ logger.error(f"Exception when getting request {script_type} script: {stacktrace}")
1647
+ raise ToolException(f"Unable to get {script_type} script for request '{request_path}': {str(e)}")
1648
+
1649
+ def parse_collection_to_flat_structure(self, collection_response: Dict[str, Any], folder_path: str = None) -> Dict[str, Any]:
1650
+ """Parse collection response into a flattened structure with path-based keys.
1651
+
1652
+ Args:
1653
+ collection_response: The Postman collection response JSON
1654
+ folder_path: Optional folder path to filter results. If provided, only items
1655
+ within this folder will be included in the output, and collection
1656
+ metadata will be excluded.
1657
+ """
1658
+ collection = collection_response.get('collection', {})
1659
+ info = collection.get('info', {})
1660
+
1661
+ # If folder_path is specified, return minimal structure focused on the folder
1662
+ if folder_path is not None:
1663
+ result = {
1664
+ "folder_path": folder_path,
1665
+ "items": {}
1666
+ }
1667
+ else:
1668
+ # Full collection structure with metadata
1669
+ result = {
1670
+ "collection_postman_id": info.get('_postman_id'),
1671
+ "name": info.get('name'),
1672
+ "updatedAt": info.get('updatedAt'),
1673
+ "createdAt": info.get('createdAt'),
1674
+ "lastUpdatedBy": info.get('lastUpdatedBy'),
1675
+ "uid": info.get('uid'),
1676
+ "items": {}
1677
+ }
1678
+
1679
+ def parse_items(items, parent_path=""):
1680
+ """Recursively parse items into flat structure."""
1681
+ for item in items:
1682
+ item_name = item.get('name', '')
1683
+ current_path = f"{parent_path}/{item_name}" if parent_path else item_name
1684
+
1685
+ # If folder_path is specified, check if we should include this item
1686
+ if folder_path is not None:
1687
+ # Check if current path is within the specified folder
1688
+ if not (current_path == folder_path or current_path.startswith(folder_path + "/")):
1689
+ # If this is a folder, we need to check if it contains the target folder
1690
+ if 'item' in item and folder_path.startswith(current_path + "/"):
1691
+ # This folder is an ancestor of the target folder, continue traversing
1692
+ parse_items(item['item'], current_path)
1693
+ continue
1694
+
1695
+ # Check if this is a folder (has 'item' property) or a request
1696
+ if 'item' in item:
1697
+ # This is a folder
1698
+ result['items'][current_path] = {
1699
+ "type": "folder",
1700
+ "id": item.get('id'),
1701
+ "uid": item.get('uid')
1702
+ }
1703
+ # Recursively parse nested items
1704
+ parse_items(item['item'], current_path)
1705
+ else:
1706
+ # This is a request
1707
+ request_info = item.get('request', {})
1708
+
1709
+ # Parse URL
1710
+ url_info = request_info.get('url', {})
1711
+ if isinstance(url_info, str):
1712
+ url = url_info
1713
+ else:
1714
+ # URL is an object with raw property
1715
+ url = url_info.get('raw', '')
1716
+
1717
+ # Parse headers
1718
+ headers = request_info.get('header', [])
1719
+
1720
+ # Parse body
1721
+ body_info = None
1722
+ body = request_info.get('body', {})
1723
+ if body:
1724
+ body_mode = body.get('mode', '')
1725
+ if body_mode == 'raw':
1726
+ try:
1727
+ raw_data = body.get('raw', '')
1728
+ if raw_data:
1729
+ body_info = {
1730
+ "type": "json",
1731
+ "data": json.loads(raw_data) if raw_data.strip() else {}
1732
+ }
1733
+ except json.JSONDecodeError:
1734
+ body_info = {
1735
+ "type": "raw",
1736
+ "data": body.get('raw', '')
1737
+ }
1738
+ elif body_mode == 'formdata':
1739
+ body_info = {
1740
+ "type": "formdata",
1741
+ "data": body.get('formdata', [])
1742
+ }
1743
+ elif body_mode == 'urlencoded':
1744
+ body_info = {
1745
+ "type": "urlencoded",
1746
+ "data": body.get('urlencoded', [])
1747
+ }
1748
+
1749
+ # Parse URL parameters
1750
+ params = []
1751
+ if isinstance(url_info, dict):
1752
+ query = url_info.get('query', [])
1753
+ for param in query:
1754
+ if isinstance(param, dict):
1755
+ params.append({
1756
+ "key": param.get('key', ''),
1757
+ "value": param.get('value', ''),
1758
+ "disabled": param.get('disabled', False)
1759
+ })
1760
+
1761
+ request_data = {
1762
+ "id": item.get('id'),
1763
+ "uid": item.get('uid'),
1764
+ "full_postman_path": current_path,
1765
+ "type": "request",
1766
+ "method": request_info.get('method', 'GET'),
1767
+ "request_url": url,
1768
+ "headers": headers,
1769
+ "params": params
1770
+ }
1771
+
1772
+ # Add body if present
1773
+ if body_info:
1774
+ request_data["body"] = body_info
1775
+
1776
+ # Add description if present
1777
+ description = request_info.get('description')
1778
+ if description:
1779
+ request_data["description"] = description
1780
+
1781
+ result['items'][current_path] = request_data
1782
+
1783
+ # Parse the top-level items
1784
+ items = collection.get('item', [])
1785
+ parse_items(items)
1786
+
1787
+ return result
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alita_sdk
3
- Version: 0.3.156
3
+ Version: 0.3.158
4
4
  Summary: SDK for building langchain agents using resources from Alita
5
5
  Author-email: Artem Rozumenko <artyom.rozumenko@gmail.com>, Mikalai Biazruchka <mikalai_biazruchka@epam.com>, Roman Mitusov <roman_mitusov@epam.com>, Ivan Krakhmaliuk <lifedjik@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -273,7 +273,7 @@ alita_sdk/tools/pandas/statsmodels/descriptive.py,sha256=APdofBnEiRhMrn6tLKwH076
273
273
  alita_sdk/tools/pandas/statsmodels/hypothesis_testing.py,sha256=fdNAayMB3W7avMfKJCcbf2_P54vUXbq8KVebOB48348,10508
274
274
  alita_sdk/tools/pandas/statsmodels/regression.py,sha256=Y1pWK4u_qzrfA740K-FX0nZ5FREGGPk8mfvykPIYoiI,9164
275
275
  alita_sdk/tools/postman/__init__.py,sha256=W0HdtACnTZw6tnzj7_qY_X5RoRyX3czcUSVaZJjBW-Y,4236
276
- alita_sdk/tools/postman/api_wrapper.py,sha256=DfcveAMRG45E3W7XzbrPTgPAFMFaKK_1mFKcYRgJuHQ,62848
276
+ alita_sdk/tools/postman/api_wrapper.py,sha256=4Hf_aOvUB1G_BdlKvNaAqnQaoCnKDph-E7v4VEwaw5Y,77933
277
277
  alita_sdk/tools/postman/postman_analysis.py,sha256=2d-Oi2UORosIePIUyncSONw9hY7dw8Zc7BQvCd4aqpg,45115
278
278
  alita_sdk/tools/pptx/__init__.py,sha256=LNSTQk0BncfdWLXAOGX2WXezG3D4qSEuYwLpokmF9iM,3438
279
279
  alita_sdk/tools/pptx/pptx_wrapper.py,sha256=yyCYcTlIY976kJ4VfPo4dyxj4yeii9j9TWP6W8ZIpN8,29195
@@ -317,8 +317,8 @@ alita_sdk/tools/zephyr_enterprise/api_wrapper.py,sha256=Ir3zHljhbZQJRJJQOBzS_GL5
317
317
  alita_sdk/tools/zephyr_enterprise/zephyr_enterprise.py,sha256=hV9LIrYfJT6oYp-ZfQR0YHflqBFPsUw2Oc55HwK0H48,6809
318
318
  alita_sdk/tools/zephyr_scale/__init__.py,sha256=2NTcdrfkx4GSegqyXhsPLsEpc4FlACuDy85b0fk6cAo,4572
319
319
  alita_sdk/tools/zephyr_scale/api_wrapper.py,sha256=UHVQUVqcBc3SZvDfO78HSuBzwAsRw2cCDQa-xMOzndE,68663
320
- alita_sdk-0.3.156.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
321
- alita_sdk-0.3.156.dist-info/METADATA,sha256=K7ufDYk_Mb_VxuqZI4YVH0kjJVfe4nYwkqH1r7zzhxA,18667
322
- alita_sdk-0.3.156.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
323
- alita_sdk-0.3.156.dist-info/top_level.txt,sha256=0vJYy5p_jK6AwVb1aqXr7Kgqgk3WDtQ6t5C-XI9zkmg,10
324
- alita_sdk-0.3.156.dist-info/RECORD,,
320
+ alita_sdk-0.3.158.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
321
+ alita_sdk-0.3.158.dist-info/METADATA,sha256=ZEtjgt10_pjYxKpcHAC5IuLXnqGyNvwv8jnxsRza7g4,18667
322
+ alita_sdk-0.3.158.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
323
+ alita_sdk-0.3.158.dist-info/top_level.txt,sha256=0vJYy5p_jK6AwVb1aqXr7Kgqgk3WDtQ6t5C-XI9zkmg,10
324
+ alita_sdk-0.3.158.dist-info/RECORD,,