11import json
22import logging
3- from typing import Any , Dict
3+ from typing import Any , Dict , List , Optional
4+
5+ from httpx import Response
46
57from .._config import Config
68from .._execution_context import ExecutionContext
7- from .._utils import Endpoint , RequestSpec , infer_bindings
9+ from .._utils import Endpoint , RequestSpec , header_folder , infer_bindings
810from ..models import Connection , ConnectionToken , EventArguments
911from ..models .connections import ConnectionTokenType
1012from ..tracing ._traced import traced
1113from ._base_service import BaseService
14+ from .folder_service import FolderService
1215
1316logger : logging .Logger = logging .getLogger ("uipath" )
1417
@@ -20,9 +23,16 @@ class ConnectionsService(BaseService):
2023 and secure token management.
2124 """
2225
23- def __init__ (self , config : Config , execution_context : ExecutionContext ) -> None :
26+ def __init__ (
27+ self ,
28+ config : Config ,
29+ execution_context : ExecutionContext ,
30+ folders_service : FolderService ,
31+ ) -> None :
2432 super ().__init__ (config = config , execution_context = execution_context )
33+ self ._folders_service = folders_service
2534
35+ @infer_bindings (resource_type = "connection" , name = "key" )
2636 @traced (
2737 name = "connections_retrieve" ,
2838 run_type = "uipath" ,
@@ -45,6 +55,117 @@ def retrieve(self, key: str) -> Connection:
4555 response = self .request (spec .method , url = spec .endpoint )
4656 return Connection .model_validate (response .json ())
4757
58+ @traced (name = "connections_list" , run_type = "uipath" )
59+ def list (
60+ self ,
61+ * ,
62+ name : Optional [str ] = None ,
63+ folder_path : Optional [str ] = None ,
64+ folder_key : Optional [str ] = None ,
65+ connector_key : Optional [str ] = None ,
66+ skip : Optional [int ] = None ,
67+ top : Optional [int ] = None ,
68+ ) -> List [Connection ]:
69+ """Lists all connections with optional filtering.
70+
71+ Args:
72+ name: Optional connection name to filter (supports partial matching)
73+ folder_path: Optional folder path for filtering connections
74+ folder_key: Optional folder key (mutually exclusive with folder_path)
75+ connector_key: Optional connector key to filter by specific connector type
76+ skip: Number of records to skip (for pagination)
77+ top: Maximum number of records to return
78+
79+ Returns:
80+ List[Connection]: List of connection instances
81+
82+ Raises:
83+ ValueError: If both folder_path and folder_key are provided, or if
84+ folder_path is provided but cannot be resolved to a folder_key
85+
86+ Examples:
87+ >>> # List all connections
88+ >>> connections = sdk.connections.list()
89+
90+ >>> # Find connections by name
91+ >>> salesforce_conns = sdk.connections.list(name="Salesforce")
92+
93+ >>> # List all Slack connections in Finance folder
94+ >>> connections = sdk.connections.list(
95+ ... folder_path="Finance",
96+ ... connector_key="uipath-slack"
97+ ... )
98+ """
99+ spec = self ._list_spec (
100+ name = name ,
101+ folder_path = folder_path ,
102+ folder_key = folder_key ,
103+ connector_key = connector_key ,
104+ skip = skip ,
105+ top = top ,
106+ )
107+ response = self .request (
108+ spec .method , url = spec .endpoint , params = spec .params , headers = spec .headers
109+ )
110+
111+ return self ._parse_and_validate_list_response (response )
112+
113+ @traced (name = "connections_list" , run_type = "uipath" )
114+ async def list_async (
115+ self ,
116+ * ,
117+ name : Optional [str ] = None ,
118+ folder_path : Optional [str ] = None ,
119+ folder_key : Optional [str ] = None ,
120+ connector_key : Optional [str ] = None ,
121+ skip : Optional [int ] = None ,
122+ top : Optional [int ] = None ,
123+ ) -> List [Connection ]:
124+ """Asynchronously lists all connections with optional filtering.
125+
126+ Args:
127+ name: Optional connection name to filter (supports partial matching)
128+ folder_path: Optional folder path for filtering connections
129+ folder_key: Optional folder key (mutually exclusive with folder_path)
130+ connector_key: Optional connector key to filter by specific connector type
131+ skip: Number of records to skip (for pagination)
132+ top: Maximum number of records to return
133+
134+ Returns:
135+ List[Connection]: List of connection instances
136+
137+ Raises:
138+ ValueError: If both folder_path and folder_key are provided, or if
139+ folder_path is provided but cannot be resolved to a folder_key
140+
141+ Examples:
142+ >>> # List all connections
143+ >>> connections = await sdk.connections.list_async()
144+
145+ >>> # Find connections by name
146+ >>> salesforce_conns = await sdk.connections.list_async(name="Salesforce")
147+
148+ >>> # List all Slack connections in Finance folder
149+ >>> connections = await sdk.connections.list_async(
150+ ... folder_path="Finance",
151+ ... connector_key="uipath-slack"
152+ ... )
153+ """
154+ spec = self ._list_spec (
155+ name = name ,
156+ folder_path = folder_path ,
157+ folder_key = folder_key ,
158+ connector_key = connector_key ,
159+ skip = skip ,
160+ top = top ,
161+ )
162+ response = await self .request_async (
163+ spec .method , url = spec .endpoint , params = spec .params , headers = spec .headers
164+ )
165+
166+ return self ._parse_and_validate_list_response (response )
167+
168+ @infer_bindings (resource_type = "connection" , name = "key" )
48169 @traced (
49170 name = "connections_retrieve" ,
50171 run_type = "uipath" ,
@@ -213,3 +334,90 @@ def _retrieve_token_spec(
213334 endpoint = Endpoint (f"/connections_/api/v1/Connections/{ key } /token" ),
214335 params = {"tokenType" : token_type .value },
215336 )
337+
338+ def _parse_and_validate_list_response (self , response : Response ) -> List [Connection ]:
339+ """Parse and validate the list response from the API.
340+
341+ Handles both OData response format (with 'value' field) and raw list responses.
342+
343+ Args:
344+ response: The HTTP response from the API
345+
346+ Returns:
347+ List of validated Connection instances
348+ """
349+ data = response .json ()
350+
351+ # Handle both OData responses (dict with 'value') and raw list responses
352+ if isinstance (data , dict ):
353+ connections_data = data .get ("value" , [])
354+ elif isinstance (data , list ):
355+ connections_data = data
356+ else :
357+ connections_data = []
358+
359+ return [Connection .model_validate (conn ) for conn in connections_data ]
360+
361+ def _list_spec (
362+ self ,
363+ name : Optional [str ] = None ,
364+ folder_path : Optional [str ] = None ,
365+ folder_key : Optional [str ] = None ,
366+ connector_key : Optional [str ] = None ,
367+ skip : Optional [int ] = None ,
368+ top : Optional [int ] = None ,
369+ ) -> RequestSpec :
370+ """Build the request specification for listing connections.
371+
372+ Args:
373+ name: Optional connection name to filter (supports partial matching)
374+ folder_path: Optional folder path for filtering connections
375+ folder_key: Optional folder key (mutually exclusive with folder_path)
376+ connector_key: Optional connector key to filter by specific connector type
377+ skip: Number of records to skip (for pagination)
378+ top: Maximum number of records to return
379+
380+ Returns:
381+ RequestSpec with endpoint, params, and headers configured
382+
383+ Raises:
384+ ValueError: If folder_path is provided but cannot be resolved to a folder_key
385+ """
386+ # Resolve folder_path to folder_key if needed
387+ resolved_folder_key = folder_key
388+ if not resolved_folder_key and folder_path :
389+ resolved_folder_key = self ._folders_service .retrieve_key (
390+ folder_path = folder_path
391+ )
392+ if not resolved_folder_key :
393+ raise ValueError (f"Folder with path '{ folder_path } ' not found" )
394+
395+ # Build OData filters
396+ filters = []
397+ if name :
398+ # Escape single quotes in name for OData
399+ escaped_name = name .replace ("'" , "''" )
400+ filters .append (f"contains(Name, '{ escaped_name } ')" )
401+ if connector_key :
402+ filters .append (f"connector/key eq '{ connector_key } '" )
403+
404+ params = {}
405+ if filters :
406+ params ["$filter" ] = " and " .join (filters )
407+ if skip is not None :
408+ params ["$skip" ] = skip
409+ if top is not None :
410+ params ["$top" ] = top
411+
412+ # Always expand connector and folder for complete information
413+ params ["$expand" ] = "connector,folder"
414+
415+ # Use header_folder which handles validation
416+ headers = header_folder (resolved_folder_key , None )
417+
418+ return RequestSpec (
419+ method = "GET" ,
420+ endpoint = Endpoint ("/connections_/api/v1/Connections" ),
421+ params = params ,
422+ headers = headers ,
423+ )
0 commit comments