alita-sdk 0.3.257__py3-none-any.whl → 0.3.562__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.
Files changed (278) hide show
  1. alita_sdk/cli/__init__.py +10 -0
  2. alita_sdk/cli/__main__.py +17 -0
  3. alita_sdk/cli/agent/__init__.py +5 -0
  4. alita_sdk/cli/agent/default.py +258 -0
  5. alita_sdk/cli/agent_executor.py +155 -0
  6. alita_sdk/cli/agent_loader.py +215 -0
  7. alita_sdk/cli/agent_ui.py +228 -0
  8. alita_sdk/cli/agents.py +3601 -0
  9. alita_sdk/cli/callbacks.py +647 -0
  10. alita_sdk/cli/cli.py +168 -0
  11. alita_sdk/cli/config.py +306 -0
  12. alita_sdk/cli/context/__init__.py +30 -0
  13. alita_sdk/cli/context/cleanup.py +198 -0
  14. alita_sdk/cli/context/manager.py +731 -0
  15. alita_sdk/cli/context/message.py +285 -0
  16. alita_sdk/cli/context/strategies.py +289 -0
  17. alita_sdk/cli/context/token_estimation.py +127 -0
  18. alita_sdk/cli/formatting.py +182 -0
  19. alita_sdk/cli/input_handler.py +419 -0
  20. alita_sdk/cli/inventory.py +1073 -0
  21. alita_sdk/cli/mcp_loader.py +315 -0
  22. alita_sdk/cli/toolkit.py +327 -0
  23. alita_sdk/cli/toolkit_loader.py +85 -0
  24. alita_sdk/cli/tools/__init__.py +43 -0
  25. alita_sdk/cli/tools/approval.py +224 -0
  26. alita_sdk/cli/tools/filesystem.py +1751 -0
  27. alita_sdk/cli/tools/planning.py +389 -0
  28. alita_sdk/cli/tools/terminal.py +414 -0
  29. alita_sdk/community/__init__.py +72 -12
  30. alita_sdk/community/inventory/__init__.py +236 -0
  31. alita_sdk/community/inventory/config.py +257 -0
  32. alita_sdk/community/inventory/enrichment.py +2137 -0
  33. alita_sdk/community/inventory/extractors.py +1469 -0
  34. alita_sdk/community/inventory/ingestion.py +3172 -0
  35. alita_sdk/community/inventory/knowledge_graph.py +1457 -0
  36. alita_sdk/community/inventory/parsers/__init__.py +218 -0
  37. alita_sdk/community/inventory/parsers/base.py +295 -0
  38. alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
  39. alita_sdk/community/inventory/parsers/go_parser.py +851 -0
  40. alita_sdk/community/inventory/parsers/html_parser.py +389 -0
  41. alita_sdk/community/inventory/parsers/java_parser.py +593 -0
  42. alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
  43. alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
  44. alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
  45. alita_sdk/community/inventory/parsers/python_parser.py +604 -0
  46. alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
  47. alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
  48. alita_sdk/community/inventory/parsers/text_parser.py +322 -0
  49. alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
  50. alita_sdk/community/inventory/patterns/__init__.py +61 -0
  51. alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
  52. alita_sdk/community/inventory/patterns/loader.py +348 -0
  53. alita_sdk/community/inventory/patterns/registry.py +198 -0
  54. alita_sdk/community/inventory/presets.py +535 -0
  55. alita_sdk/community/inventory/retrieval.py +1403 -0
  56. alita_sdk/community/inventory/toolkit.py +173 -0
  57. alita_sdk/community/inventory/toolkit_utils.py +176 -0
  58. alita_sdk/community/inventory/visualize.py +1370 -0
  59. alita_sdk/configurations/__init__.py +11 -0
  60. alita_sdk/configurations/ado.py +148 -2
  61. alita_sdk/configurations/azure_search.py +1 -1
  62. alita_sdk/configurations/bigquery.py +1 -1
  63. alita_sdk/configurations/bitbucket.py +94 -2
  64. alita_sdk/configurations/browser.py +18 -0
  65. alita_sdk/configurations/carrier.py +19 -0
  66. alita_sdk/configurations/confluence.py +130 -1
  67. alita_sdk/configurations/delta_lake.py +1 -1
  68. alita_sdk/configurations/figma.py +76 -5
  69. alita_sdk/configurations/github.py +65 -1
  70. alita_sdk/configurations/gitlab.py +81 -0
  71. alita_sdk/configurations/google_places.py +17 -0
  72. alita_sdk/configurations/jira.py +103 -0
  73. alita_sdk/configurations/openapi.py +111 -0
  74. alita_sdk/configurations/postman.py +1 -1
  75. alita_sdk/configurations/qtest.py +72 -3
  76. alita_sdk/configurations/report_portal.py +115 -0
  77. alita_sdk/configurations/salesforce.py +19 -0
  78. alita_sdk/configurations/service_now.py +1 -12
  79. alita_sdk/configurations/sharepoint.py +167 -0
  80. alita_sdk/configurations/sonar.py +18 -0
  81. alita_sdk/configurations/sql.py +20 -0
  82. alita_sdk/configurations/testio.py +101 -0
  83. alita_sdk/configurations/testrail.py +88 -0
  84. alita_sdk/configurations/xray.py +94 -1
  85. alita_sdk/configurations/zephyr_enterprise.py +94 -1
  86. alita_sdk/configurations/zephyr_essential.py +95 -0
  87. alita_sdk/runtime/clients/artifact.py +21 -4
  88. alita_sdk/runtime/clients/client.py +458 -67
  89. alita_sdk/runtime/clients/mcp_discovery.py +342 -0
  90. alita_sdk/runtime/clients/mcp_manager.py +262 -0
  91. alita_sdk/runtime/clients/sandbox_client.py +352 -0
  92. alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
  93. alita_sdk/runtime/langchain/assistant.py +183 -43
  94. alita_sdk/runtime/langchain/constants.py +647 -1
  95. alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
  96. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +209 -31
  97. alita_sdk/runtime/langchain/document_loaders/AlitaImageLoader.py +1 -1
  98. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
  99. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +10 -3
  100. alita_sdk/runtime/langchain/document_loaders/AlitaMarkdownLoader.py +66 -0
  101. alita_sdk/runtime/langchain/document_loaders/AlitaPDFLoader.py +79 -10
  102. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +52 -15
  103. alita_sdk/runtime/langchain/document_loaders/AlitaPythonLoader.py +9 -0
  104. alita_sdk/runtime/langchain/document_loaders/AlitaTableLoader.py +1 -4
  105. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +15 -2
  106. alita_sdk/runtime/langchain/document_loaders/ImageParser.py +30 -0
  107. alita_sdk/runtime/langchain/document_loaders/constants.py +189 -41
  108. alita_sdk/runtime/langchain/interfaces/llm_processor.py +4 -2
  109. alita_sdk/runtime/langchain/langraph_agent.py +407 -92
  110. alita_sdk/runtime/langchain/utils.py +102 -8
  111. alita_sdk/runtime/llms/preloaded.py +2 -6
  112. alita_sdk/runtime/models/mcp_models.py +61 -0
  113. alita_sdk/runtime/skills/__init__.py +91 -0
  114. alita_sdk/runtime/skills/callbacks.py +498 -0
  115. alita_sdk/runtime/skills/discovery.py +540 -0
  116. alita_sdk/runtime/skills/executor.py +610 -0
  117. alita_sdk/runtime/skills/input_builder.py +371 -0
  118. alita_sdk/runtime/skills/models.py +330 -0
  119. alita_sdk/runtime/skills/registry.py +355 -0
  120. alita_sdk/runtime/skills/skill_runner.py +330 -0
  121. alita_sdk/runtime/toolkits/__init__.py +28 -0
  122. alita_sdk/runtime/toolkits/application.py +14 -4
  123. alita_sdk/runtime/toolkits/artifact.py +24 -9
  124. alita_sdk/runtime/toolkits/datasource.py +13 -6
  125. alita_sdk/runtime/toolkits/mcp.py +780 -0
  126. alita_sdk/runtime/toolkits/planning.py +178 -0
  127. alita_sdk/runtime/toolkits/skill_router.py +238 -0
  128. alita_sdk/runtime/toolkits/subgraph.py +11 -6
  129. alita_sdk/runtime/toolkits/tools.py +314 -70
  130. alita_sdk/runtime/toolkits/vectorstore.py +11 -5
  131. alita_sdk/runtime/tools/__init__.py +24 -0
  132. alita_sdk/runtime/tools/application.py +16 -4
  133. alita_sdk/runtime/tools/artifact.py +367 -33
  134. alita_sdk/runtime/tools/data_analysis.py +183 -0
  135. alita_sdk/runtime/tools/function.py +100 -4
  136. alita_sdk/runtime/tools/graph.py +81 -0
  137. alita_sdk/runtime/tools/image_generation.py +218 -0
  138. alita_sdk/runtime/tools/llm.py +1013 -177
  139. alita_sdk/runtime/tools/loop.py +3 -1
  140. alita_sdk/runtime/tools/loop_output.py +3 -1
  141. alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
  142. alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
  143. alita_sdk/runtime/tools/mcp_server_tool.py +3 -1
  144. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  145. alita_sdk/runtime/tools/planning/models.py +246 -0
  146. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  147. alita_sdk/runtime/tools/router.py +2 -1
  148. alita_sdk/runtime/tools/sandbox.py +375 -0
  149. alita_sdk/runtime/tools/skill_router.py +776 -0
  150. alita_sdk/runtime/tools/tool.py +3 -1
  151. alita_sdk/runtime/tools/vectorstore.py +69 -65
  152. alita_sdk/runtime/tools/vectorstore_base.py +163 -90
  153. alita_sdk/runtime/utils/AlitaCallback.py +137 -21
  154. alita_sdk/runtime/utils/mcp_client.py +492 -0
  155. alita_sdk/runtime/utils/mcp_oauth.py +361 -0
  156. alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
  157. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  158. alita_sdk/runtime/utils/streamlit.py +41 -14
  159. alita_sdk/runtime/utils/toolkit_utils.py +28 -9
  160. alita_sdk/runtime/utils/utils.py +48 -0
  161. alita_sdk/tools/__init__.py +135 -37
  162. alita_sdk/tools/ado/__init__.py +2 -2
  163. alita_sdk/tools/ado/repos/__init__.py +15 -19
  164. alita_sdk/tools/ado/repos/repos_wrapper.py +12 -20
  165. alita_sdk/tools/ado/test_plan/__init__.py +26 -8
  166. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -28
  167. alita_sdk/tools/ado/wiki/__init__.py +27 -12
  168. alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -40
  169. alita_sdk/tools/ado/work_item/__init__.py +27 -12
  170. alita_sdk/tools/ado/work_item/ado_wrapper.py +95 -11
  171. alita_sdk/tools/advanced_jira_mining/__init__.py +12 -8
  172. alita_sdk/tools/aws/delta_lake/__init__.py +14 -11
  173. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  174. alita_sdk/tools/azure_ai/search/__init__.py +13 -8
  175. alita_sdk/tools/base/tool.py +5 -1
  176. alita_sdk/tools/base_indexer_toolkit.py +454 -110
  177. alita_sdk/tools/bitbucket/__init__.py +27 -19
  178. alita_sdk/tools/bitbucket/api_wrapper.py +285 -27
  179. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +5 -5
  180. alita_sdk/tools/browser/__init__.py +41 -16
  181. alita_sdk/tools/browser/crawler.py +3 -1
  182. alita_sdk/tools/browser/utils.py +15 -6
  183. alita_sdk/tools/carrier/__init__.py +18 -17
  184. alita_sdk/tools/carrier/backend_reports_tool.py +8 -4
  185. alita_sdk/tools/carrier/excel_reporter.py +8 -4
  186. alita_sdk/tools/chunkers/__init__.py +3 -1
  187. alita_sdk/tools/chunkers/code/codeparser.py +1 -1
  188. alita_sdk/tools/chunkers/sematic/json_chunker.py +2 -1
  189. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  190. alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
  191. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  192. alita_sdk/tools/cloud/aws/__init__.py +11 -7
  193. alita_sdk/tools/cloud/azure/__init__.py +11 -7
  194. alita_sdk/tools/cloud/gcp/__init__.py +11 -7
  195. alita_sdk/tools/cloud/k8s/__init__.py +11 -7
  196. alita_sdk/tools/code/linter/__init__.py +9 -8
  197. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  198. alita_sdk/tools/code/sonar/__init__.py +20 -13
  199. alita_sdk/tools/code_indexer_toolkit.py +199 -0
  200. alita_sdk/tools/confluence/__init__.py +21 -14
  201. alita_sdk/tools/confluence/api_wrapper.py +197 -58
  202. alita_sdk/tools/confluence/loader.py +14 -2
  203. alita_sdk/tools/custom_open_api/__init__.py +11 -5
  204. alita_sdk/tools/elastic/__init__.py +10 -8
  205. alita_sdk/tools/elitea_base.py +546 -64
  206. alita_sdk/tools/figma/__init__.py +11 -8
  207. alita_sdk/tools/figma/api_wrapper.py +352 -153
  208. alita_sdk/tools/github/__init__.py +17 -17
  209. alita_sdk/tools/github/api_wrapper.py +9 -26
  210. alita_sdk/tools/github/github_client.py +81 -12
  211. alita_sdk/tools/github/schemas.py +2 -1
  212. alita_sdk/tools/github/tool.py +5 -1
  213. alita_sdk/tools/gitlab/__init__.py +18 -13
  214. alita_sdk/tools/gitlab/api_wrapper.py +224 -80
  215. alita_sdk/tools/gitlab_org/__init__.py +13 -10
  216. alita_sdk/tools/google/bigquery/__init__.py +13 -13
  217. alita_sdk/tools/google/bigquery/tool.py +5 -1
  218. alita_sdk/tools/google_places/__init__.py +20 -11
  219. alita_sdk/tools/jira/__init__.py +21 -11
  220. alita_sdk/tools/jira/api_wrapper.py +315 -168
  221. alita_sdk/tools/keycloak/__init__.py +10 -8
  222. alita_sdk/tools/localgit/__init__.py +8 -3
  223. alita_sdk/tools/localgit/local_git.py +62 -54
  224. alita_sdk/tools/localgit/tool.py +5 -1
  225. alita_sdk/tools/memory/__init__.py +38 -14
  226. alita_sdk/tools/non_code_indexer_toolkit.py +7 -2
  227. alita_sdk/tools/ocr/__init__.py +10 -8
  228. alita_sdk/tools/openapi/__init__.py +281 -108
  229. alita_sdk/tools/openapi/api_wrapper.py +883 -0
  230. alita_sdk/tools/openapi/tool.py +20 -0
  231. alita_sdk/tools/pandas/__init__.py +18 -11
  232. alita_sdk/tools/pandas/api_wrapper.py +40 -45
  233. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  234. alita_sdk/tools/postman/__init__.py +10 -11
  235. alita_sdk/tools/postman/api_wrapper.py +19 -8
  236. alita_sdk/tools/postman/postman_analysis.py +8 -1
  237. alita_sdk/tools/pptx/__init__.py +10 -10
  238. alita_sdk/tools/qtest/__init__.py +21 -14
  239. alita_sdk/tools/qtest/api_wrapper.py +1784 -88
  240. alita_sdk/tools/rally/__init__.py +12 -10
  241. alita_sdk/tools/report_portal/__init__.py +22 -16
  242. alita_sdk/tools/salesforce/__init__.py +21 -16
  243. alita_sdk/tools/servicenow/__init__.py +20 -16
  244. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  245. alita_sdk/tools/sharepoint/__init__.py +16 -14
  246. alita_sdk/tools/sharepoint/api_wrapper.py +179 -39
  247. alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
  248. alita_sdk/tools/sharepoint/utils.py +8 -2
  249. alita_sdk/tools/slack/__init__.py +11 -7
  250. alita_sdk/tools/sql/__init__.py +21 -19
  251. alita_sdk/tools/sql/api_wrapper.py +71 -23
  252. alita_sdk/tools/testio/__init__.py +20 -13
  253. alita_sdk/tools/testrail/__init__.py +12 -11
  254. alita_sdk/tools/testrail/api_wrapper.py +214 -46
  255. alita_sdk/tools/utils/__init__.py +28 -4
  256. alita_sdk/tools/utils/content_parser.py +182 -62
  257. alita_sdk/tools/utils/text_operations.py +254 -0
  258. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
  259. alita_sdk/tools/xray/__init__.py +17 -14
  260. alita_sdk/tools/xray/api_wrapper.py +58 -113
  261. alita_sdk/tools/yagmail/__init__.py +8 -3
  262. alita_sdk/tools/zephyr/__init__.py +11 -7
  263. alita_sdk/tools/zephyr_enterprise/__init__.py +15 -9
  264. alita_sdk/tools/zephyr_enterprise/api_wrapper.py +30 -15
  265. alita_sdk/tools/zephyr_essential/__init__.py +15 -10
  266. alita_sdk/tools/zephyr_essential/api_wrapper.py +297 -54
  267. alita_sdk/tools/zephyr_essential/client.py +6 -4
  268. alita_sdk/tools/zephyr_scale/__init__.py +12 -8
  269. alita_sdk/tools/zephyr_scale/api_wrapper.py +39 -31
  270. alita_sdk/tools/zephyr_squad/__init__.py +11 -7
  271. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/METADATA +184 -37
  272. alita_sdk-0.3.562.dist-info/RECORD +450 -0
  273. alita_sdk-0.3.562.dist-info/entry_points.txt +2 -0
  274. alita_sdk/tools/bitbucket/tools.py +0 -304
  275. alita_sdk-0.3.257.dist-info/RECORD +0 -343
  276. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/WHEEL +0 -0
  277. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/licenses/LICENSE +0 -0
  278. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/top_level.txt +0 -0
@@ -84,4 +84,68 @@ class GithubConfiguration(BaseModel):
84
84
  "Authentication is misconfigured: provide either Token (access_token), "
85
85
  "Password (username + password), App private key (app_id + app_private_key), "
86
86
  "or leave all blank for anonymous access."
87
- )
87
+ )
88
+
89
+ @staticmethod
90
+ def check_connection(settings: dict) -> str | None:
91
+ """
92
+ Check GitHub connection using provided settings.
93
+ Returns None if connection is successful, error message otherwise.
94
+ """
95
+ import requests
96
+ from requests.auth import HTTPBasicAuth
97
+ import jwt
98
+ import time
99
+
100
+ base_url = settings.get('base_url', 'https://api.github.com')
101
+ access_token = settings.get('access_token')
102
+ username = settings.get('username')
103
+ password = settings.get('password')
104
+ app_id = settings.get('app_id')
105
+ app_private_key = settings.get('app_private_key')
106
+
107
+ # if all auth methods are None or empty, allow anonymous access
108
+ if not any([access_token, (username and password), (app_id and app_private_key)]):
109
+ return None
110
+
111
+ headers = {'Accept': 'application/vnd.github.v3+json'}
112
+ auth = None
113
+
114
+ try:
115
+ # Determine authentication method
116
+ if access_token:
117
+ headers['Authorization'] = f'token {access_token}'
118
+ elif username and password:
119
+ auth = HTTPBasicAuth(username, password)
120
+ elif app_id and app_private_key:
121
+ # Generate JWT for GitHub App authentication
122
+ payload = {
123
+ 'iat': int(time.time()),
124
+ 'exp': int(time.time()) + 600, # 10 minutes
125
+ 'iss': app_id
126
+ }
127
+ jwt_token = jwt.encode(payload, app_private_key, algorithm='RS256')
128
+ headers['Authorization'] = f'Bearer {jwt_token}'
129
+
130
+ # Test connection with user endpoint
131
+ response = requests.get(f'{base_url}/user', headers=headers, auth=auth, timeout=10)
132
+
133
+ if response.status_code == 200:
134
+ return None
135
+ elif response.status_code == 401:
136
+ return "Authentication failed: Invalid credentials"
137
+ elif response.status_code == 403:
138
+ return "Access forbidden: Check your permissions"
139
+ elif response.status_code == 404:
140
+ return "GitHub API endpoint not found"
141
+ else:
142
+ return f"Connection failed with status {response.status_code}: {response.text}"
143
+
144
+ except requests.exceptions.ConnectionError:
145
+ return "Connection error: Unable to reach GitHub API"
146
+ except requests.exceptions.Timeout:
147
+ return "Connection timeout: GitHub API did not respond in time"
148
+ except jwt.InvalidKeyError:
149
+ return "Invalid private key format for GitHub App authentication"
150
+ except Exception as e:
151
+ return f"Unexpected error: {str(e)}"
@@ -29,3 +29,84 @@ class GitlabConfiguration(BaseModel):
29
29
  )
30
30
  url: str = Field(description="GitLab URL")
31
31
  private_token: SecretStr = Field(description="GitLab private token")
32
+
33
+ @staticmethod
34
+ def check_connection(settings: dict) -> str | None:
35
+ """
36
+ Check the connection to GitLab.
37
+
38
+ Args:
39
+ settings: Dictionary containing GitLab configuration
40
+ - url: GitLab instance URL (required)
41
+ - private_token: GitLab private token for authentication (required)
42
+
43
+ Returns:
44
+ None if connection successful, error message string if failed
45
+ """
46
+ import requests
47
+
48
+ # Validate url
49
+ url = settings.get("url", "").strip()
50
+ if not url:
51
+ return "GitLab URL is required"
52
+
53
+ # Normalize URL - remove trailing slashes
54
+ url = url.rstrip("/")
55
+
56
+ # Basic URL validation
57
+ if not url.startswith(("http://", "https://")):
58
+ return "GitLab URL must start with http:// or https://"
59
+
60
+ # Validate private_token
61
+ private_token = settings.get("private_token")
62
+ if not private_token:
63
+ return "GitLab private token is required"
64
+
65
+ # Extract token value if it's a SecretStr
66
+ token_value = private_token.get_secret_value() if hasattr(private_token, 'get_secret_value') else private_token
67
+
68
+ if not token_value or not str(token_value).strip():
69
+ return "GitLab private token cannot be empty"
70
+
71
+ # Test connection using /api/v4/user endpoint
72
+ # This endpoint returns current authenticated user info
73
+ test_url = f"{url}/api/v4/user"
74
+
75
+ # GitLab supports both PRIVATE-TOKEN header and Authorization Bearer
76
+ # Using PRIVATE-TOKEN is GitLab-specific and more explicit
77
+ headers = {
78
+ "PRIVATE-TOKEN": str(token_value).strip()
79
+ }
80
+
81
+ try:
82
+ response = requests.get(
83
+ test_url,
84
+ headers=headers,
85
+ timeout=10
86
+ )
87
+
88
+ # Check response status
89
+ if response.status_code == 200:
90
+ # Successfully connected and authenticated
91
+ return None
92
+ elif response.status_code == 401:
93
+ return "Authentication failed: invalid private token"
94
+ elif response.status_code == 403:
95
+ return "Access forbidden: token lacks required permissions"
96
+ elif response.status_code == 404:
97
+ return "GitLab API endpoint not found: verify the GitLab URL"
98
+ else:
99
+ return f"GitLab API returned status code {response.status_code}"
100
+
101
+ except requests.exceptions.SSLError as e:
102
+ if 'Hostname mismatch' in str(e):
103
+ return "GitLab API endpoint not found: verify the GitLab URL"
104
+ return f"SSL certificate verification failed: {str(e)}"
105
+ except requests.exceptions.ConnectionError:
106
+ return f"Cannot connect to GitLab at {url}: connection refused"
107
+ except requests.exceptions.Timeout:
108
+ return f"Connection to GitLab at {url} timed out"
109
+ except requests.exceptions.RequestException as e:
110
+ return f"Error connecting to GitLab: {str(e)}"
111
+ except Exception as e:
112
+ return f"Unexpected error: {str(e)}"
@@ -0,0 +1,17 @@
1
+ from pydantic import BaseModel, ConfigDict, Field, SecretStr
2
+
3
+
4
+ class GooglePlacesConfiguration(BaseModel):
5
+ model_config = ConfigDict(
6
+ json_schema_extra={
7
+ "metadata": {
8
+ "label": "Google Places",
9
+ "icon_url": "google.svg",
10
+ "section": "credentials",
11
+ "type": "google_places",
12
+ "categories": ["other"],
13
+ "extra_categories": ["google", "places", "maps", "location", "geocoding"],
14
+ }
15
+ }
16
+ )
17
+ api_key: SecretStr = Field(description="Google Places API Key")
@@ -35,3 +35,106 @@ class JiraConfiguration(BaseModel):
35
35
  username: Optional[str] = Field(description="Jira Username", default=None)
36
36
  api_key: Optional[SecretStr] = Field(description="Jira API Key", default=None)
37
37
  token: Optional[SecretStr] = Field(description="Jira Token", default=None)
38
+
39
+ @staticmethod
40
+ def check_connection(settings: dict) -> str | None:
41
+ """
42
+ Check Jira connection using provided settings.
43
+ Returns None if connection is successful, error message otherwise.
44
+
45
+ Tests authentication by calling the /rest/api/latest/myself endpoint,
46
+ which returns information about the currently authenticated user.
47
+ """
48
+ import requests
49
+ from requests.auth import HTTPBasicAuth
50
+
51
+ # Extract and validate settings
52
+ base_url = settings.get('base_url', '').rstrip('/')
53
+ username = settings.get('username')
54
+ api_key = settings.get('api_key')
55
+ token = settings.get('token')
56
+
57
+ # Validate base URL
58
+ if not base_url:
59
+ return "Base URL is required"
60
+
61
+ if not base_url.startswith(('http://', 'https://')):
62
+ return "Base URL must start with http:// or https://"
63
+
64
+ # Validate authentication - at least one method must be provided
65
+ has_basic_auth = bool(username and api_key)
66
+ has_token = bool(token and str(token).strip())
67
+
68
+ if not (has_basic_auth or has_token):
69
+ return "Authentication required: Provide either username + API key, or bearer token"
70
+
71
+ # Setup authentication headers
72
+ headers = {'Accept': 'application/json'}
73
+ auth = None
74
+
75
+ if has_token:
76
+ # Bearer token authentication
77
+ token_value = token.get_secret_value() if hasattr(token, 'get_secret_value') else token
78
+ headers['Authorization'] = f'Bearer {token_value}'
79
+ elif has_basic_auth:
80
+ # Basic authentication
81
+ api_key_value = api_key.get_secret_value() if hasattr(api_key, 'get_secret_value') else api_key
82
+ auth = HTTPBasicAuth(username, api_key_value)
83
+
84
+ # Build API endpoint - using 'latest' for version independence
85
+ api_endpoint = f"{base_url}/rest/api/latest/myself"
86
+
87
+ try:
88
+ # Make authenticated request to verify credentials
89
+ response = requests.get(
90
+ api_endpoint,
91
+ headers=headers,
92
+ auth=auth,
93
+ timeout=10
94
+ )
95
+
96
+ # Handle different response codes
97
+ if response.status_code == 200:
98
+ return None # Success - credentials are valid
99
+
100
+ elif response.status_code == 401:
101
+ # Authentication failed
102
+ if has_token:
103
+ return "Authentication failed: Invalid bearer token"
104
+ else:
105
+ return "Authentication failed: Invalid username or API key"
106
+
107
+ elif response.status_code == 403:
108
+ # Authenticated but insufficient permissions
109
+ return "Access forbidden: Your account has insufficient permissions to access Jira API"
110
+
111
+ elif response.status_code == 404:
112
+ # API endpoint not found - likely wrong URL
113
+ return "Jira API endpoint not found: Verify your base URL (e.g., 'https://yourinstance.atlassian.net')"
114
+
115
+ else:
116
+ # Other HTTP errors - try to extract Jira error messages
117
+ error_detail = ""
118
+ try:
119
+ error_json = response.json()
120
+ if 'errorMessages' in error_json and error_json['errorMessages']:
121
+ error_detail = ": " + ", ".join(error_json['errorMessages'])
122
+ elif 'message' in error_json:
123
+ error_detail = f": {error_json['message']}"
124
+ except:
125
+ pass
126
+
127
+ return f"Connection failed with status {response.status_code}{error_detail}"
128
+
129
+ except requests.exceptions.SSLError:
130
+ return "SSL certificate verification failed: Check your Jira URL or network settings"
131
+ except requests.exceptions.ConnectionError:
132
+ return "Connection error: Unable to reach Jira server - check URL and network connectivity"
133
+ except requests.exceptions.Timeout:
134
+ return "Connection timeout: Jira server did not respond within 10 seconds"
135
+ except requests.exceptions.MissingSchema:
136
+ return "Invalid URL format: URL must include protocol (http:// or https://)"
137
+ except requests.exceptions.InvalidURL:
138
+ return "Invalid URL format: Please check your Jira base URL"
139
+ except Exception as e:
140
+ return f"Unexpected error: {str(e)}"
@@ -0,0 +1,111 @@
1
+ from typing import Any, Literal, Optional
2
+ from pydantic import BaseModel, ConfigDict, Field, SecretStr, model_validator
3
+
4
+
5
+ class OpenApiConfiguration(BaseModel):
6
+ model_config = ConfigDict(
7
+ extra='allow',
8
+ json_schema_extra={
9
+ "metadata": {
10
+ "label": "OpenAPI",
11
+ "icon_url": "openapi.svg",
12
+ "categories": ["integrations"],
13
+ "type": "openapi",
14
+ "extra_categories": ["api", "openapi", "swagger"],
15
+ "sections": {
16
+ "auth": {
17
+ "required": False,
18
+ "subsections": [
19
+ {
20
+ "name": "API Key",
21
+ "fields": ["api_key", "auth_type", "custom_header_name"],
22
+ },
23
+ {
24
+ "name": "OAuth",
25
+ "fields": [
26
+ "client_id",
27
+ "client_secret",
28
+ "auth_url",
29
+ "token_url",
30
+ "scope",
31
+ "method",
32
+ ],
33
+ },
34
+ ],
35
+ },
36
+ },
37
+ "section": "credentials",
38
+ }
39
+ }
40
+ )
41
+
42
+ api_key: Optional[SecretStr] = Field(
43
+ default=None,
44
+ description=(
45
+ "API key value (stored as a secret). Used when selecting 'API Key' authentication subsection."
46
+ ),
47
+ )
48
+ auth_type: Optional[Literal['Basic', 'Bearer', 'Custom']] = Field(
49
+ default='Bearer',
50
+ description=(
51
+ "How to apply the API key. "
52
+ "- 'Bearer': sets 'Authorization: Bearer <api_key>' "
53
+ "- 'Basic': sets 'Authorization: Basic <api_key>' "
54
+ "- 'custom': sets '<custom_header_name>: <api_key>'"
55
+ ),
56
+ )
57
+ custom_header_name: Optional[str] = Field(
58
+ default=None,
59
+ description="Custom header name to use when auth_type='custom' (e.g. 'X-Api-Key').",
60
+ json_schema_extra={'visible_when': {'field': 'auth_type', 'value': 'custom'}},
61
+ )
62
+
63
+ client_id: Optional[str] = Field(default=None, description='OAuth client ID')
64
+ client_secret: Optional[SecretStr] = Field(default=None, description='OAuth client secret (stored as a secret)')
65
+ auth_url: Optional[str] = Field(default=None, description='OAuth authorization URL')
66
+ token_url: Optional[str] = Field(default=None, description='OAuth token URL')
67
+ scope: Optional[str] = Field(default=None, description='OAuth scope(s)')
68
+ method: Optional[Literal['default', 'Basic']] = Field(
69
+ default='default',
70
+ description=(
71
+ "Token exchange method. 'default' uses standard POST body; 'Basic' uses a Basic authorization header."
72
+ ),
73
+ )
74
+
75
+ @model_validator(mode='before')
76
+ @classmethod
77
+ def _validate_auth_consistency(cls, values):
78
+ if not isinstance(values, dict):
79
+ return values
80
+
81
+ # OAuth: if any OAuth field is provided, require the key ones.
82
+ has_any_oauth = any(
83
+ (values.get('client_id'), values.get('client_secret'), values.get('auth_url'), values.get('token_url'))
84
+ )
85
+ if has_any_oauth:
86
+ missing = []
87
+ if not values.get('client_id'):
88
+ missing.append('client_id')
89
+ if not values.get('client_secret'):
90
+ missing.append('client_secret')
91
+ if not values.get('token_url'):
92
+ missing.append('token_url')
93
+ if missing:
94
+ raise ValueError(f"OAuth is misconfigured; missing: {', '.join(missing)}")
95
+
96
+ # API key: if auth_type is custom, custom_header_name must be present.
97
+ auth_type = values.get('auth_type')
98
+ if isinstance(auth_type, str) and auth_type.strip().lower() == 'custom' and values.get('api_key'):
99
+ if not values.get('custom_header_name'):
100
+ raise ValueError("custom_header_name is required when auth_type='custom'")
101
+
102
+ return values
103
+
104
+ @staticmethod
105
+ def check_connection(settings: dict) -> str | None:
106
+ """Best-effort validation for OpenAPI credentials.
107
+
108
+ This model is intended to store reusable credentials only.
109
+ Spec/base_url validation happens at toolkit configuration level.
110
+ """
111
+ return None
@@ -22,7 +22,7 @@ class PostmanConfiguration(BaseModel):
22
22
  },
23
23
  "section": "credentials",
24
24
  "type": "postman",
25
- "categories": ["api testing"],
25
+ "categories": ["other"],
26
26
  "extra_categories": ["postman", "api", "testing", "collection"],
27
27
  }
28
28
  }
@@ -1,5 +1,6 @@
1
- from typing import Optional
1
+ import re
2
2
 
3
+ import requests
3
4
  from pydantic import BaseModel, ConfigDict, Field, SecretStr
4
5
 
5
6
 
@@ -16,6 +17,74 @@ class QtestConfiguration(BaseModel):
16
17
  }
17
18
  }
18
19
  )
19
- base_url: str = Field(description="QTest base url")
20
- qtest_api_token: Optional[SecretStr] = Field(description="QTest API token", default=None)
20
+ base_url: str = Field(description="QTest base URL")
21
+ qtest_api_token: SecretStr = Field(description="QTest API token")
22
+
23
+ @staticmethod
24
+ def check_connection(settings: dict) -> str | None:
25
+ """Check connectivity and credentials for qTest.
26
+
27
+ Strategy:
28
+ - Validate token against an auth-required endpoint (so an incorrect token is detected).
29
+
30
+ Returns:
31
+ None if successful, otherwise a short actionable error message.
32
+ """
33
+ base_url_input = settings.get("base_url")
34
+ base_url = base_url_input.strip() if isinstance(base_url_input, str) else ""
35
+ if not base_url:
36
+ return "QTest base URL is required"
37
+
38
+ if not base_url.startswith(("http://", "https://")):
39
+ return "QTest base URL must start with http:// or https://"
40
+
41
+ base_url = base_url.rstrip("/")
42
+ # If user pasted /api/v3 (or similar), strip it so we can build canonical API URLs.
43
+ base_url = re.sub(r"/api/v\d+/?$", "", base_url, flags=re.IGNORECASE)
44
+
45
+ token = settings.get("qtest_api_token")
46
+ if token is None:
47
+ return "QTest API token is required"
48
+ token_value = token.get_secret_value() if hasattr(token, "get_secret_value") else str(token)
49
+ if not token_value or not token_value.strip():
50
+ return "QTest API token cannot be empty"
51
+
52
+ headers = {
53
+ "Authorization": f"Bearer {token_value}",
54
+ "Content-Type": "application/json",
55
+ }
56
+
57
+ # Auth-required endpoint to validate the token.
58
+ # /projects works on v3 and requires auth in typical qTest deployments.
59
+ token_check_url = f"{base_url}/api/v3/projects?pageSize=1&page=1"
60
+
61
+ try:
62
+ resp = requests.get(token_check_url, headers=headers, timeout=10)
63
+ if resp.status_code == 200:
64
+ return None
65
+ elif resp.status_code == 401:
66
+ return "Invalid or expired QTest API token"
67
+ elif resp.status_code == 403:
68
+ return "Access forbidden - token lacks required permissions"
69
+ elif resp.status_code == 404:
70
+ return "QTest API not found (404) - verify base URL (do not include /api/v3)"
71
+ elif resp.status_code == 429:
72
+ return "Rate limited (429) - please try again later"
73
+ elif 500 <= resp.status_code <= 599:
74
+ return f"QTest service error (HTTP {resp.status_code})"
75
+ else:
76
+ return f"QTest connection failed (HTTP {resp.status_code})"
77
+
78
+ except requests.exceptions.Timeout:
79
+ return "Connection timeout - qTest did not respond within 10 seconds"
80
+ except requests.exceptions.ConnectionError:
81
+ return "Connection error - unable to reach qTest. Check base URL and network."
82
+ except requests.exceptions.SSLError:
83
+ return "SSL error - certificate verification failed"
84
+ except requests.exceptions.RequestException as e:
85
+ return f"Request failed: {str(e)}"
86
+ except Exception:
87
+ return "Unexpected error during qTest connection check"
88
+
89
+
21
90
 
@@ -0,0 +1,115 @@
1
+ from urllib.parse import quote, urlparse, urlunparse
2
+
3
+ import requests
4
+ from pydantic import BaseModel, ConfigDict, Field, SecretStr
5
+
6
+
7
+ class ReportPortalConfiguration(BaseModel):
8
+ model_config = ConfigDict(
9
+ json_schema_extra={
10
+ "metadata": {
11
+ "label": "Report Portal",
12
+ "icon_url": "report_portal.svg",
13
+ "section": "credentials",
14
+ "type": "report_portal",
15
+ "categories": ["testing"],
16
+ "extra_categories": ["report portal", "testing", "automation", "reports"],
17
+ }
18
+ }
19
+ )
20
+ project: str = Field(description="Report Portal Project Name")
21
+ endpoint: str = Field(description="Report Portal Endpoint URL")
22
+ api_key: SecretStr = Field(description="Report Portal API Key")
23
+
24
+ @staticmethod
25
+ def check_connection(settings: dict) -> str | None:
26
+ """Check the connection to ReportPortal.
27
+
28
+ Validates:
29
+ - endpoint URL format and reachability
30
+ - API key (token) via an auth-required endpoint
31
+ - project access (because most ReportPortal APIs are scoped to a project)
32
+
33
+ Returns:
34
+ None if connection successful, error message string otherwise
35
+ """
36
+ endpoint_in = settings.get("endpoint")
37
+ endpoint = endpoint_in.strip() if isinstance(endpoint_in, str) else ""
38
+ if not endpoint:
39
+ return "Endpoint is required"
40
+
41
+ if not endpoint.startswith(("http://", "https://")):
42
+ return "Endpoint must start with http:// or https://"
43
+
44
+ # Normalize: remove query/fragment and trailing slash.
45
+ parsed = urlparse(endpoint)
46
+ endpoint = urlunparse(parsed._replace(query="", fragment="")).rstrip("/")
47
+
48
+ # If user pasted an API URL, normalize back to base endpoint.
49
+ # Common pastes: .../api/v1 or .../api/v1/<project>
50
+ # lowered = endpoint.lower()
51
+ # for suffix in ("/api/v1", "/api"):
52
+ # if lowered.endswith(suffix):
53
+ # endpoint = endpoint[: -len(suffix)].rstrip("/")
54
+ # lowered = endpoint.lower()
55
+ # break
56
+
57
+ project_in = settings.get("project")
58
+ project = project_in.strip() if isinstance(project_in, str) else ""
59
+ if not project:
60
+ return "Project is required"
61
+
62
+ api_key = settings.get("api_key")
63
+ if api_key is None:
64
+ return "API key is required"
65
+ api_key_value = api_key.get_secret_value() if hasattr(api_key, "get_secret_value") else str(api_key)
66
+ if not api_key_value or not api_key_value.strip():
67
+ return "API key cannot be empty"
68
+
69
+ # Auth-required endpoint for verification.
70
+ # /user endpoint validates the token and project context.
71
+ project_encoded = quote(project, safe="")
72
+ test_url = f"{endpoint}/api/v1/project/{project_encoded}"
73
+
74
+ try:
75
+ resp = requests.get(
76
+ test_url,
77
+ headers={"Authorization": f"Bearer {api_key_value}"},
78
+ timeout=10,
79
+ )
80
+
81
+ if resp.status_code == 200:
82
+ return None
83
+ if resp.status_code == 401:
84
+ return "Invalid API key"
85
+ if resp.status_code == 403:
86
+ return "Access forbidden - API key has no access to this project"
87
+ if resp.status_code == 404:
88
+ return "API endpoint not found (404) - verify endpoint URL and project name"
89
+ if resp.status_code == 429:
90
+ return "Rate limited (429) - please try again later"
91
+ if 500 <= resp.status_code <= 599:
92
+ return f"ReportPortal service error (HTTP {resp.status_code})"
93
+ return f"Connection failed (HTTP {resp.status_code})"
94
+
95
+ except requests.exceptions.Timeout:
96
+ return "Connection timeout - ReportPortal did not respond within 10 seconds"
97
+ except requests.exceptions.SSLError as e:
98
+ if "Hostname mismatch" in str(e):
99
+ return "API endpoint not found - verify endpoint URL and project name"
100
+ return "SSL error - certificate verification failed"
101
+ except requests.exceptions.ConnectionError:
102
+ return "Connection error - unable to reach ReportPortal. Check endpoint URL and network."
103
+ except requests.exceptions.RequestException as e:
104
+ return f"Request failed: {str(e)}"
105
+ except Exception:
106
+ return "Unexpected error during ReportPortal connection check"
107
+
108
+ if __name__ == '__main__':
109
+ settings = {
110
+ "endpoint": "https://reportportal.epam.com",
111
+ "project": "epm-alta",
112
+ "api_key": "my-api-key_U1kF0zFvToqcweG3x552cI8pnYkMqszgtht_LHGZhpxwrxDl5nXlmrSf_JLEE8jy",
113
+ }
114
+
115
+ print(ReportPortalConfiguration.check_connection(settings))
@@ -0,0 +1,19 @@
1
+ from pydantic import BaseModel, ConfigDict, Field, SecretStr
2
+
3
+
4
+ class SalesforceConfiguration(BaseModel):
5
+ model_config = ConfigDict(
6
+ json_schema_extra={
7
+ "metadata": {
8
+ "label": "Salesforce",
9
+ "icon_url": "salesforce.svg",
10
+ "section": "credentials",
11
+ "type": "salesforce",
12
+ "categories": ["other"],
13
+ "extra_categories": ["salesforce", "crm", "sales", "customer"],
14
+ }
15
+ }
16
+ )
17
+ client_id: str = Field(description="Salesforce Client ID")
18
+ client_secret: SecretStr = Field(description="Salesforce Client Secret")
19
+ base_url: str = Field(description="Salesforce Base URL")
@@ -9,20 +9,9 @@ class ServiceNowConfiguration(BaseModel):
9
9
  "metadata": {
10
10
  "label": "ServiceNow",
11
11
  "icon_url": "servicenow.svg",
12
- "sections": {
13
- "auth": {
14
- "required": True,
15
- "subsections": [
16
- {
17
- "name": "Username & Password",
18
- "fields": ["username", "password"]
19
- }
20
- ]
21
- },
22
- },
23
12
  "section": "credentials",
24
13
  "type": "service_now",
25
- "categories": ["service management"],
14
+ "categories": ["other"],
26
15
  "extra_categories": ["servicenow", "itsm", "service management", "incident"],
27
16
  }
28
17
  }