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.
- alita_sdk/tools/postman/api_wrapper.py +672 -369
- {alita_sdk-0.3.156.dist-info → alita_sdk-0.3.158.dist-info}/METADATA +1 -1
- {alita_sdk-0.3.156.dist-info → alita_sdk-0.3.158.dist-info}/RECORD +6 -6
- {alita_sdk-0.3.156.dist-info → alita_sdk-0.3.158.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.156.dist-info → alita_sdk-0.3.158.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.156.dist-info → alita_sdk-0.3.158.dist-info}/top_level.txt +0 -0
@@ -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
|
-
|
48
|
-
"
|
49
|
-
|
50
|
-
description="
|
51
|
-
)
|
52
|
-
|
53
|
-
|
54
|
-
|
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",
|
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
|
-
|
287
|
-
|
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
|
307
|
-
"args_schema":
|
308
|
-
"ref": self.
|
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
|
314
|
-
"args_schema":
|
315
|
-
"ref": self.
|
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": "
|
319
|
-
"mode": "
|
320
|
-
"description": "Get
|
321
|
-
"args_schema":
|
322
|
-
"ref": self.
|
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": "
|
333
|
-
"mode": "
|
334
|
-
"description": "Analyze
|
335
|
-
"args_schema":
|
336
|
-
"ref": self.
|
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
|
-
|
672
|
+
collection_response = self._make_request(
|
599
673
|
'GET', f'/collections/{self.collection_id}')
|
600
|
-
|
601
|
-
|
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(
|
609
|
-
"
|
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
|
620
|
-
"""
|
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
|
-
|
623
|
-
|
624
|
-
|
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
|
627
|
-
|
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
|
-
|
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
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
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.
|
753
|
+
improvements = self.analyzer.generate_improvements(analysis)
|
655
754
|
analysis["improvements"] = improvements
|
656
755
|
analysis["improvement_count"] = len(improvements)
|
657
756
|
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
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
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
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
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
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
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
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
|
-
|
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
|
804
|
+
logger.error(f"Exception when analyzing {scope}: {stacktrace}")
|
694
805
|
raise ToolException(
|
695
|
-
f"Unable to analyze
|
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
|
899
|
-
|
900
|
-
|
901
|
-
|
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
|
-
|
1040
|
+
folder_update["name"] = name
|
914
1041
|
if description is not None:
|
915
|
-
|
1042
|
+
folder_update["description"] = description
|
916
1043
|
if auth is not None:
|
917
|
-
|
918
|
-
|
919
|
-
#
|
920
|
-
|
921
|
-
|
922
|
-
|
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
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
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
|
1081
|
-
response = self._make_request('PUT', f'/collections/{self.collection_id}',
|
1082
|
-
json=
|
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
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
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
|
1108
|
-
response = self._make_request('PUT', f'/collections/{self.collection_id}',
|
1109
|
-
json=
|
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
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
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
|
1135
|
-
response = self._make_request('PUT', f'/collections/{self.collection_id}',
|
1136
|
-
json=
|
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
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
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
|
1162
|
-
response = self._make_request('PUT', f'/collections/{self.collection_id}',
|
1163
|
-
json=
|
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
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
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
|
1189
|
-
response = self._make_request('PUT', f'/collections/{self.collection_id}',
|
1190
|
-
json=
|
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
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
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
|
1216
|
-
response = self._make_request('PUT', f'/collections/{self.collection_id}',
|
1217
|
-
json=
|
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
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
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
|
1243
|
-
response = self._make_request('PUT', f'/collections/{self.collection_id}',
|
1244
|
-
json=
|
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
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
if
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
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
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
if
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
1320
|
-
|
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
|
-
|
1450
|
-
|
1451
|
-
|
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
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
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"
|
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.
|
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=
|
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.
|
321
|
-
alita_sdk-0.3.
|
322
|
-
alita_sdk-0.3.
|
323
|
-
alita_sdk-0.3.
|
324
|
-
alita_sdk-0.3.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|