alita-sdk 0.3.257__py3-none-any.whl → 0.3.584__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.

Potentially problematic release.


This version of alita-sdk might be problematic. Click here for more details.

Files changed (281) 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 +3794 -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 +323 -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 +493 -105
  110. alita_sdk/runtime/langchain/utils.py +118 -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 +25 -9
  124. alita_sdk/runtime/toolkits/datasource.py +13 -6
  125. alita_sdk/runtime/toolkits/mcp.py +782 -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 +1032 -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/constants.py +5 -1
  155. alita_sdk/runtime/utils/mcp_client.py +492 -0
  156. alita_sdk/runtime/utils/mcp_oauth.py +361 -0
  157. alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
  158. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  159. alita_sdk/runtime/utils/streamlit.py +41 -14
  160. alita_sdk/runtime/utils/toolkit_utils.py +28 -9
  161. alita_sdk/runtime/utils/utils.py +48 -0
  162. alita_sdk/tools/__init__.py +135 -37
  163. alita_sdk/tools/ado/__init__.py +2 -2
  164. alita_sdk/tools/ado/repos/__init__.py +16 -19
  165. alita_sdk/tools/ado/repos/repos_wrapper.py +12 -20
  166. alita_sdk/tools/ado/test_plan/__init__.py +27 -8
  167. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -28
  168. alita_sdk/tools/ado/wiki/__init__.py +28 -12
  169. alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -40
  170. alita_sdk/tools/ado/work_item/__init__.py +28 -12
  171. alita_sdk/tools/ado/work_item/ado_wrapper.py +95 -11
  172. alita_sdk/tools/advanced_jira_mining/__init__.py +13 -8
  173. alita_sdk/tools/aws/delta_lake/__init__.py +15 -11
  174. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  175. alita_sdk/tools/azure_ai/search/__init__.py +14 -8
  176. alita_sdk/tools/base/tool.py +5 -1
  177. alita_sdk/tools/base_indexer_toolkit.py +454 -110
  178. alita_sdk/tools/bitbucket/__init__.py +28 -19
  179. alita_sdk/tools/bitbucket/api_wrapper.py +285 -27
  180. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +5 -5
  181. alita_sdk/tools/browser/__init__.py +41 -16
  182. alita_sdk/tools/browser/crawler.py +3 -1
  183. alita_sdk/tools/browser/utils.py +15 -6
  184. alita_sdk/tools/carrier/__init__.py +18 -17
  185. alita_sdk/tools/carrier/backend_reports_tool.py +8 -4
  186. alita_sdk/tools/carrier/excel_reporter.py +8 -4
  187. alita_sdk/tools/chunkers/__init__.py +3 -1
  188. alita_sdk/tools/chunkers/code/codeparser.py +1 -1
  189. alita_sdk/tools/chunkers/sematic/json_chunker.py +2 -1
  190. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  191. alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
  192. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  193. alita_sdk/tools/cloud/aws/__init__.py +12 -7
  194. alita_sdk/tools/cloud/azure/__init__.py +12 -7
  195. alita_sdk/tools/cloud/gcp/__init__.py +12 -7
  196. alita_sdk/tools/cloud/k8s/__init__.py +12 -7
  197. alita_sdk/tools/code/linter/__init__.py +10 -8
  198. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  199. alita_sdk/tools/code/sonar/__init__.py +21 -13
  200. alita_sdk/tools/code_indexer_toolkit.py +199 -0
  201. alita_sdk/tools/confluence/__init__.py +22 -14
  202. alita_sdk/tools/confluence/api_wrapper.py +197 -58
  203. alita_sdk/tools/confluence/loader.py +14 -2
  204. alita_sdk/tools/custom_open_api/__init__.py +12 -5
  205. alita_sdk/tools/elastic/__init__.py +11 -8
  206. alita_sdk/tools/elitea_base.py +546 -64
  207. alita_sdk/tools/figma/__init__.py +60 -11
  208. alita_sdk/tools/figma/api_wrapper.py +1400 -167
  209. alita_sdk/tools/figma/figma_client.py +73 -0
  210. alita_sdk/tools/figma/toon_tools.py +2748 -0
  211. alita_sdk/tools/github/__init__.py +18 -17
  212. alita_sdk/tools/github/api_wrapper.py +9 -26
  213. alita_sdk/tools/github/github_client.py +81 -12
  214. alita_sdk/tools/github/schemas.py +2 -1
  215. alita_sdk/tools/github/tool.py +5 -1
  216. alita_sdk/tools/gitlab/__init__.py +19 -13
  217. alita_sdk/tools/gitlab/api_wrapper.py +256 -80
  218. alita_sdk/tools/gitlab_org/__init__.py +14 -10
  219. alita_sdk/tools/google/bigquery/__init__.py +14 -13
  220. alita_sdk/tools/google/bigquery/tool.py +5 -1
  221. alita_sdk/tools/google_places/__init__.py +21 -11
  222. alita_sdk/tools/jira/__init__.py +22 -11
  223. alita_sdk/tools/jira/api_wrapper.py +315 -168
  224. alita_sdk/tools/keycloak/__init__.py +11 -8
  225. alita_sdk/tools/localgit/__init__.py +9 -3
  226. alita_sdk/tools/localgit/local_git.py +62 -54
  227. alita_sdk/tools/localgit/tool.py +5 -1
  228. alita_sdk/tools/memory/__init__.py +38 -14
  229. alita_sdk/tools/non_code_indexer_toolkit.py +7 -2
  230. alita_sdk/tools/ocr/__init__.py +11 -8
  231. alita_sdk/tools/openapi/__init__.py +491 -106
  232. alita_sdk/tools/openapi/api_wrapper.py +1357 -0
  233. alita_sdk/tools/openapi/tool.py +20 -0
  234. alita_sdk/tools/pandas/__init__.py +20 -12
  235. alita_sdk/tools/pandas/api_wrapper.py +40 -45
  236. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  237. alita_sdk/tools/postman/__init__.py +11 -11
  238. alita_sdk/tools/postman/api_wrapper.py +19 -8
  239. alita_sdk/tools/postman/postman_analysis.py +8 -1
  240. alita_sdk/tools/pptx/__init__.py +11 -10
  241. alita_sdk/tools/qtest/__init__.py +22 -14
  242. alita_sdk/tools/qtest/api_wrapper.py +1784 -88
  243. alita_sdk/tools/rally/__init__.py +13 -10
  244. alita_sdk/tools/report_portal/__init__.py +23 -16
  245. alita_sdk/tools/salesforce/__init__.py +22 -16
  246. alita_sdk/tools/servicenow/__init__.py +21 -16
  247. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  248. alita_sdk/tools/sharepoint/__init__.py +17 -14
  249. alita_sdk/tools/sharepoint/api_wrapper.py +179 -39
  250. alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
  251. alita_sdk/tools/sharepoint/utils.py +8 -2
  252. alita_sdk/tools/slack/__init__.py +13 -8
  253. alita_sdk/tools/sql/__init__.py +22 -19
  254. alita_sdk/tools/sql/api_wrapper.py +71 -23
  255. alita_sdk/tools/testio/__init__.py +21 -13
  256. alita_sdk/tools/testrail/__init__.py +13 -11
  257. alita_sdk/tools/testrail/api_wrapper.py +214 -46
  258. alita_sdk/tools/utils/__init__.py +28 -4
  259. alita_sdk/tools/utils/content_parser.py +241 -55
  260. alita_sdk/tools/utils/text_operations.py +254 -0
  261. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
  262. alita_sdk/tools/xray/__init__.py +18 -14
  263. alita_sdk/tools/xray/api_wrapper.py +58 -113
  264. alita_sdk/tools/yagmail/__init__.py +9 -3
  265. alita_sdk/tools/zephyr/__init__.py +12 -7
  266. alita_sdk/tools/zephyr_enterprise/__init__.py +16 -9
  267. alita_sdk/tools/zephyr_enterprise/api_wrapper.py +30 -15
  268. alita_sdk/tools/zephyr_essential/__init__.py +16 -10
  269. alita_sdk/tools/zephyr_essential/api_wrapper.py +297 -54
  270. alita_sdk/tools/zephyr_essential/client.py +6 -4
  271. alita_sdk/tools/zephyr_scale/__init__.py +13 -8
  272. alita_sdk/tools/zephyr_scale/api_wrapper.py +39 -31
  273. alita_sdk/tools/zephyr_squad/__init__.py +12 -7
  274. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/METADATA +184 -37
  275. alita_sdk-0.3.584.dist-info/RECORD +452 -0
  276. alita_sdk-0.3.584.dist-info/entry_points.txt +2 -0
  277. alita_sdk/tools/bitbucket/tools.py +0 -304
  278. alita_sdk-0.3.257.dist-info/RECORD +0 -343
  279. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/WHEEL +0 -0
  280. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/licenses/LICENSE +0 -0
  281. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/top_level.txt +0 -0
@@ -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
  }
@@ -0,0 +1,167 @@
1
+ import requests
2
+ from office365.onedrive.sharepoint_settings import SharepointSettings
3
+ from pydantic import BaseModel, ConfigDict, Field, SecretStr
4
+
5
+
6
+ class SharepointConfiguration(BaseModel):
7
+ model_config = ConfigDict(
8
+ json_schema_extra={
9
+ "metadata": {
10
+ "label": "SharePoint",
11
+ "icon_url": "sharepoint.svg",
12
+ "section": "credentials",
13
+ "type": "sharepoint",
14
+ "categories": ["office"],
15
+ "extra_categories": ["sharepoint", "microsoft", "documents", "collaboration"],
16
+ }
17
+ }
18
+ )
19
+ client_id: str = Field(description="SharePoint Client ID")
20
+ client_secret: SecretStr = Field(description="SharePoint Client Secret")
21
+ site_url: str = Field(description="SharePoint Site URL")
22
+
23
+ @staticmethod
24
+ def check_connection(settings: dict) -> str | None:
25
+ """
26
+ Test the connection to SharePoint API using OAuth2 client credentials.
27
+
28
+ Args:
29
+ settings: Dictionary containing 'client_id', 'client_secret', and 'site_url' (all required)
30
+
31
+ Returns:
32
+ None if connection is successful, error message string otherwise
33
+ """
34
+ # Validate client_id
35
+ client_id = settings.get("client_id")
36
+ if client_id is None or client_id == "":
37
+ if client_id == "":
38
+ return "Client ID cannot be empty"
39
+ return "Client ID is required"
40
+
41
+ if not isinstance(client_id, str):
42
+ return "Client ID must be a string"
43
+
44
+ client_id = client_id.strip()
45
+ if not client_id:
46
+ return "Client ID cannot be empty"
47
+
48
+ # Validate client_secret
49
+ client_secret = settings.get("client_secret")
50
+ if client_secret is None:
51
+ return "Client secret is required"
52
+
53
+ # Extract secret value if it's a SecretStr
54
+ if hasattr(client_secret, "get_secret_value"):
55
+ client_secret = client_secret.get_secret_value()
56
+
57
+ if not client_secret or not client_secret.strip():
58
+ return "Client secret cannot be empty"
59
+
60
+ # Validate site_url
61
+ site_url = settings.get("site_url")
62
+ if site_url is None or site_url == "":
63
+ if site_url == "":
64
+ return "Site URL cannot be empty"
65
+ return "Site URL is required"
66
+
67
+ if not isinstance(site_url, str):
68
+ return "Site URL must be a string"
69
+
70
+ site_url = site_url.strip()
71
+ if not site_url:
72
+ return "Site URL cannot be empty"
73
+
74
+ if not site_url.startswith(("http://", "https://")):
75
+ return "Site URL must start with http:// or https://"
76
+
77
+ # Remove trailing slash for consistency
78
+ site_url = site_url.rstrip("/")
79
+
80
+ # Extract tenant and resource from site URL
81
+ # Expected format: https://<tenant>.sharepoint.com/sites/<site>
82
+ try:
83
+ if ".sharepoint.com" not in site_url:
84
+ return "Site URL must be a valid SharePoint URL (*.sharepoint.com)"
85
+
86
+ # Extract tenant (e.g., "contoso" from "contoso.sharepoint.com")
87
+ parts = site_url.split("//")[1].split(".")
88
+ if len(parts) < 3:
89
+ return "Invalid SharePoint URL format"
90
+ tenant = parts[0]
91
+
92
+ # Build token endpoint
93
+ token_url = f"https://accounts.accesscontrol.windows.net/{tenant}.onmicrosoft.com/tokens/OAuth/2"
94
+
95
+ # Build resource (the site URL with /_api appended)
96
+ resource = f"{site_url.split('/sites/')[0]}@{site_url.split('//')[1].split('/')[0].split('.')[0]}"
97
+
98
+ except Exception:
99
+ return "Failed to parse SharePoint URL - ensure it's in format: https://<tenant>.sharepoint.com/sites/<site>"
100
+
101
+ try:
102
+ # Step 1: Get OAuth2 access token using client credentials
103
+ token_response = requests.post(
104
+ token_url,
105
+ data={
106
+ "grant_type": "client_credentials",
107
+ "client_id": f"{client_id}@{tenant}.onmicrosoft.com",
108
+ "client_secret": client_secret,
109
+ "resource": f"00000003-0000-0ff1-ce00-000000000000/{site_url.split('//')[1].split('/')[0]}@{tenant}.onmicrosoft.com"
110
+ },
111
+ timeout=10,
112
+ )
113
+
114
+ if token_response.status_code == 400:
115
+ try:
116
+ error_data = token_response.json()
117
+ error_desc = error_data.get("error_description", "")
118
+ if "not found in the directory" in error_desc.lower():
119
+ return "Invalid client ID. Please check if you provide a correct client ID and try again."
120
+ elif "client_secret" in error_desc.lower():
121
+ return "Invalid client secret"
122
+ else:
123
+ return f"OAuth2 authentication failed: {error_desc}"
124
+ except Exception:
125
+ return "Invalid client credentials"
126
+
127
+ elif token_response.status_code == 401:
128
+ return "Invalid client secret provided. Please check if you provide a correct client secret and try again."
129
+ elif token_response.status_code != 200:
130
+ return f"Failed to obtain access token (status {token_response.status_code})"
131
+
132
+ # Extract access token
133
+ try:
134
+ token_data = token_response.json()
135
+ access_token = token_data.get("access_token")
136
+ if not access_token:
137
+ return "No access token received from SharePoint"
138
+ except Exception:
139
+ return "Failed to parse token response"
140
+
141
+ # Step 2: Test the access token by calling SharePoint API
142
+ api_url = f"{site_url}/_api/web"
143
+ api_response = requests.get(
144
+ api_url,
145
+ headers={"Authorization": f"Bearer {access_token}"},
146
+ timeout=10,
147
+ )
148
+
149
+ if api_response.status_code == 200:
150
+ return None # Connection successful
151
+ elif api_response.status_code == 401:
152
+ return "Access token is invalid or expired"
153
+ elif api_response.status_code == 403:
154
+ return "Access forbidden - client may lack required permissions for this site"
155
+ elif api_response.status_code == 404:
156
+ return f"Site not found or not accessible: {site_url}"
157
+ else:
158
+ return f"SharePoint API request failed with status {api_response.status_code}"
159
+
160
+ except requests.exceptions.Timeout:
161
+ return "Connection timeout - SharePoint is not responding"
162
+ except requests.exceptions.ConnectionError:
163
+ return "Connection error - unable to reach SharePoint"
164
+ except requests.exceptions.RequestException as e:
165
+ return f"Request failed: {str(e)}"
166
+ except Exception as e:
167
+ return f"Unexpected error: {str(e)}"
@@ -0,0 +1,18 @@
1
+ from pydantic import BaseModel, ConfigDict, Field, SecretStr
2
+
3
+
4
+ class SonarConfiguration(BaseModel):
5
+ model_config = ConfigDict(
6
+ json_schema_extra={
7
+ "metadata": {
8
+ "label": "Sonar",
9
+ "icon_url": "sonar-icon.svg",
10
+ "section": "credentials",
11
+ "type": "sonar",
12
+ "categories": ["development"],
13
+ "extra_categories": ["code quality", "code security", "code coverage", "quality", "sonarqube"],
14
+ }
15
+ }
16
+ )
17
+ url: str = Field(description="SonarQube Server URL")
18
+ sonar_token: SecretStr = Field(description="SonarQube user token for authentication")
@@ -0,0 +1,20 @@
1
+ from pydantic import BaseModel, ConfigDict, Field, SecretStr
2
+
3
+
4
+ class SqlConfiguration(BaseModel):
5
+ model_config = ConfigDict(
6
+ json_schema_extra={
7
+ "metadata": {
8
+ "label": "SQL",
9
+ "icon_url": "sql.svg",
10
+ "section": "credentials",
11
+ "type": "sql",
12
+ "categories": ["development"],
13
+ "extra_categories": ["sql", "database", "data", "query"],
14
+ }
15
+ }
16
+ )
17
+ host: str = Field(description="Database host")
18
+ port: int = Field(description="Database port", default=None)
19
+ username: str = Field(description="Database username")
20
+ password: SecretStr = Field(description="Database password", json_schema_extra={'secret': True})
@@ -0,0 +1,101 @@
1
+ import requests
2
+ from pydantic import BaseModel, ConfigDict, Field, SecretStr
3
+
4
+
5
+ class TestIOConfiguration(BaseModel):
6
+ model_config = ConfigDict(
7
+ json_schema_extra={
8
+ "metadata": {
9
+ "label": "TestIO",
10
+ "icon_url": "testio.svg",
11
+ "section": "credentials",
12
+ "type": "testio",
13
+ "categories": ["testing"],
14
+ "extra_categories": ["testio", "testing", "crowd testing", "qa"],
15
+ }
16
+ }
17
+ )
18
+ endpoint: str = Field(description="TestIO endpoint")
19
+ api_key: SecretStr = Field(description="TestIO API Key")
20
+
21
+ @staticmethod
22
+ def check_connection(settings: dict) -> str | None:
23
+ """
24
+ Test the connection to TestIO API.
25
+
26
+ Args:
27
+ settings: Dictionary containing 'endpoint' and 'api_key' (both required)
28
+
29
+ Returns:
30
+ None if connection is successful, error message string otherwise
31
+ """
32
+ endpoint = settings.get("endpoint")
33
+ if endpoint is None or endpoint == "":
34
+ if endpoint == "":
35
+ return "Endpoint cannot be empty"
36
+ return "Endpoint is required"
37
+
38
+ # Validate endpoint format
39
+ if not isinstance(endpoint, str):
40
+ return "Endpoint must be a string"
41
+
42
+ endpoint = endpoint.strip()
43
+ if not endpoint:
44
+ return "Endpoint cannot be empty"
45
+ if not endpoint.startswith(("http://", "https://")):
46
+ return "Endpoint must start with http:// or https://"
47
+
48
+ # Remove trailing slash for consistency
49
+ endpoint = endpoint.rstrip("/")
50
+
51
+ api_key = settings.get("api_key")
52
+ if api_key is None:
53
+ return "API key is required"
54
+
55
+ # Extract secret value if it's a SecretStr
56
+ if hasattr(api_key, "get_secret_value"):
57
+ api_key = api_key.get_secret_value()
58
+
59
+ # Validate API key is not empty
60
+ if not api_key or not api_key.strip():
61
+ return "API key cannot be empty"
62
+
63
+ # Verification strategy:
64
+ # Use an auth-required endpoint and a single, explicit auth scheme:
65
+ # Authorization: Token <token>
66
+ url = f"{endpoint}/customer/v2/products"
67
+
68
+ try:
69
+ resp = TestIOConfiguration._get_with_token(url, api_key)
70
+
71
+ if resp.status_code == 200:
72
+ return None # Connection successful
73
+ if resp.status_code == 401:
74
+ return "Invalid token"
75
+ if resp.status_code == 403:
76
+ return "Access forbidden - token has no access to /customer/v2/products"
77
+ if resp.status_code == 404:
78
+ return "Invalid endpoint. Verify TestIO base endpoint."
79
+ if resp.status_code == 429:
80
+ return "Rate limited - please try again later"
81
+ if 500 <= resp.status_code <= 599:
82
+ return f"TestIO service error (HTTP {resp.status_code})"
83
+ return f"Connection failed (HTTP {resp.status_code})"
84
+
85
+ except requests.exceptions.Timeout:
86
+ return "Connection timeout - TestIO did not respond within 10 seconds"
87
+ except requests.exceptions.ConnectionError:
88
+ return "Connection error - unable to reach TestIO. Check endpoint URL and network."
89
+ except requests.exceptions.RequestException as e:
90
+ return f"Request failed: {str(e)}"
91
+ except Exception:
92
+ return "Unexpected error during TestIO connection check"
93
+
94
+ @staticmethod
95
+ def _get_with_token(url: str, token: str) -> requests.Response:
96
+ """Perform an authenticated GET using `Authorization: Token <token>`."""
97
+ return requests.get(
98
+ url,
99
+ headers={"Authorization": f"Token {token}"},
100
+ timeout=10,
101
+ )
@@ -19,3 +19,91 @@ class TestRailConfiguration(BaseModel):
19
19
  url: str = Field(description="Testrail URL")
20
20
  email: str = Field(description="TestRail Email")
21
21
  password: SecretStr = Field(description="TestRail Password")
22
+
23
+ @staticmethod
24
+ def check_connection(settings: dict) -> str | None:
25
+ """
26
+ Check the connection to TestRail.
27
+
28
+ Args:
29
+ settings: Dictionary containing TestRail configuration
30
+ - url: TestRail instance URL (required)
31
+ - email: User email for authentication (required)
32
+ - password: Password or API key for authentication (required)
33
+
34
+ Returns:
35
+ None if connection successful, error message string if failed
36
+ """
37
+ import requests
38
+ from requests.auth import HTTPBasicAuth
39
+
40
+ # Validate url
41
+ url = settings.get("url", "").strip()
42
+ if not url:
43
+ return "TestRail URL is required"
44
+
45
+ # Normalize URL - remove trailing slashes
46
+ url = url.rstrip("/")
47
+
48
+ # Basic URL validation
49
+ if not url.startswith(("http://", "https://")):
50
+ return "TestRail URL must start with http:// or https://"
51
+
52
+ # Validate email
53
+ email = settings.get("email", "").strip()
54
+ if not email:
55
+ return "TestRail email is required"
56
+
57
+ # Validate password
58
+ password = settings.get("password")
59
+ if not password:
60
+ return "TestRail password is required"
61
+
62
+ # Extract password value if it's a SecretStr
63
+ password_value = password.get_secret_value() if hasattr(password, 'get_secret_value') else password
64
+
65
+ if not password_value or not password_value.strip():
66
+ return "TestRail password cannot be empty"
67
+
68
+ # Test connection using /index.php?/api/v2/get_user_by_email endpoint
69
+ # This endpoint returns user info and validates authentication
70
+ test_url = f"{url}/index.php?/api/v2/get_user_by_email&email={email}"
71
+
72
+ try:
73
+ response = requests.get(
74
+ test_url,
75
+ auth=HTTPBasicAuth(email, password_value),
76
+ timeout=10
77
+ )
78
+
79
+ # Check response status
80
+ if response.status_code == 200:
81
+ # Successfully connected and authenticated
82
+ return None
83
+ elif response.status_code == 401:
84
+ return "Authentication failed: invalid email or password"
85
+ elif response.status_code == 403:
86
+ return "Access forbidden: check user permissions"
87
+ elif response.status_code == 404:
88
+ return "TestRail API endpoint not found: verify the TestRail URL"
89
+ elif response.status_code == 400:
90
+ # Could be invalid email format or other bad request
91
+ try:
92
+ error_data = response.json()
93
+ error_msg = error_data.get("error", "Bad request")
94
+ return f"Bad request: {error_msg}"
95
+ except:
96
+ return "Bad request: check email format and URL"
97
+ else:
98
+ return f"TestRail API returned status code {response.status_code}"
99
+
100
+ except requests.exceptions.SSLError as e:
101
+ return f"SSL certificate verification failed: {str(e)}"
102
+ except requests.exceptions.ConnectionError:
103
+ return f"Cannot connect to TestRail at {url}: connection refused"
104
+ except requests.exceptions.Timeout:
105
+ return f"Connection to TestRail at {url} timed out"
106
+ except requests.exceptions.RequestException as e:
107
+ return f"Error connecting to TestRail: {str(e)}"
108
+ except Exception as e:
109
+ return f"Unexpected error: {str(e)}"