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.
- alita_sdk/cli/__init__.py +10 -0
- alita_sdk/cli/__main__.py +17 -0
- alita_sdk/cli/agent/__init__.py +5 -0
- alita_sdk/cli/agent/default.py +258 -0
- alita_sdk/cli/agent_executor.py +155 -0
- alita_sdk/cli/agent_loader.py +215 -0
- alita_sdk/cli/agent_ui.py +228 -0
- alita_sdk/cli/agents.py +3601 -0
- alita_sdk/cli/callbacks.py +647 -0
- alita_sdk/cli/cli.py +168 -0
- alita_sdk/cli/config.py +306 -0
- alita_sdk/cli/context/__init__.py +30 -0
- alita_sdk/cli/context/cleanup.py +198 -0
- alita_sdk/cli/context/manager.py +731 -0
- alita_sdk/cli/context/message.py +285 -0
- alita_sdk/cli/context/strategies.py +289 -0
- alita_sdk/cli/context/token_estimation.py +127 -0
- alita_sdk/cli/formatting.py +182 -0
- alita_sdk/cli/input_handler.py +419 -0
- alita_sdk/cli/inventory.py +1073 -0
- alita_sdk/cli/mcp_loader.py +315 -0
- alita_sdk/cli/toolkit.py +327 -0
- alita_sdk/cli/toolkit_loader.py +85 -0
- alita_sdk/cli/tools/__init__.py +43 -0
- alita_sdk/cli/tools/approval.py +224 -0
- alita_sdk/cli/tools/filesystem.py +1751 -0
- alita_sdk/cli/tools/planning.py +389 -0
- alita_sdk/cli/tools/terminal.py +414 -0
- alita_sdk/community/__init__.py +72 -12
- alita_sdk/community/inventory/__init__.py +236 -0
- alita_sdk/community/inventory/config.py +257 -0
- alita_sdk/community/inventory/enrichment.py +2137 -0
- alita_sdk/community/inventory/extractors.py +1469 -0
- alita_sdk/community/inventory/ingestion.py +3172 -0
- alita_sdk/community/inventory/knowledge_graph.py +1457 -0
- alita_sdk/community/inventory/parsers/__init__.py +218 -0
- alita_sdk/community/inventory/parsers/base.py +295 -0
- alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
- alita_sdk/community/inventory/parsers/go_parser.py +851 -0
- alita_sdk/community/inventory/parsers/html_parser.py +389 -0
- alita_sdk/community/inventory/parsers/java_parser.py +593 -0
- alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
- alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
- alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
- alita_sdk/community/inventory/parsers/python_parser.py +604 -0
- alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
- alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
- alita_sdk/community/inventory/parsers/text_parser.py +322 -0
- alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
- alita_sdk/community/inventory/patterns/__init__.py +61 -0
- alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
- alita_sdk/community/inventory/patterns/loader.py +348 -0
- alita_sdk/community/inventory/patterns/registry.py +198 -0
- alita_sdk/community/inventory/presets.py +535 -0
- alita_sdk/community/inventory/retrieval.py +1403 -0
- alita_sdk/community/inventory/toolkit.py +173 -0
- alita_sdk/community/inventory/toolkit_utils.py +176 -0
- alita_sdk/community/inventory/visualize.py +1370 -0
- alita_sdk/configurations/__init__.py +11 -0
- alita_sdk/configurations/ado.py +148 -2
- alita_sdk/configurations/azure_search.py +1 -1
- alita_sdk/configurations/bigquery.py +1 -1
- alita_sdk/configurations/bitbucket.py +94 -2
- alita_sdk/configurations/browser.py +18 -0
- alita_sdk/configurations/carrier.py +19 -0
- alita_sdk/configurations/confluence.py +130 -1
- alita_sdk/configurations/delta_lake.py +1 -1
- alita_sdk/configurations/figma.py +76 -5
- alita_sdk/configurations/github.py +65 -1
- alita_sdk/configurations/gitlab.py +81 -0
- alita_sdk/configurations/google_places.py +17 -0
- alita_sdk/configurations/jira.py +103 -0
- alita_sdk/configurations/openapi.py +111 -0
- alita_sdk/configurations/postman.py +1 -1
- alita_sdk/configurations/qtest.py +72 -3
- alita_sdk/configurations/report_portal.py +115 -0
- alita_sdk/configurations/salesforce.py +19 -0
- alita_sdk/configurations/service_now.py +1 -12
- alita_sdk/configurations/sharepoint.py +167 -0
- alita_sdk/configurations/sonar.py +18 -0
- alita_sdk/configurations/sql.py +20 -0
- alita_sdk/configurations/testio.py +101 -0
- alita_sdk/configurations/testrail.py +88 -0
- alita_sdk/configurations/xray.py +94 -1
- alita_sdk/configurations/zephyr_enterprise.py +94 -1
- alita_sdk/configurations/zephyr_essential.py +95 -0
- alita_sdk/runtime/clients/artifact.py +21 -4
- alita_sdk/runtime/clients/client.py +458 -67
- alita_sdk/runtime/clients/mcp_discovery.py +342 -0
- alita_sdk/runtime/clients/mcp_manager.py +262 -0
- alita_sdk/runtime/clients/sandbox_client.py +352 -0
- alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
- alita_sdk/runtime/langchain/assistant.py +183 -43
- alita_sdk/runtime/langchain/constants.py +647 -1
- alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
- alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +209 -31
- alita_sdk/runtime/langchain/document_loaders/AlitaImageLoader.py +1 -1
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +10 -3
- alita_sdk/runtime/langchain/document_loaders/AlitaMarkdownLoader.py +66 -0
- alita_sdk/runtime/langchain/document_loaders/AlitaPDFLoader.py +79 -10
- alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +52 -15
- alita_sdk/runtime/langchain/document_loaders/AlitaPythonLoader.py +9 -0
- alita_sdk/runtime/langchain/document_loaders/AlitaTableLoader.py +1 -4
- alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +15 -2
- alita_sdk/runtime/langchain/document_loaders/ImageParser.py +30 -0
- alita_sdk/runtime/langchain/document_loaders/constants.py +189 -41
- alita_sdk/runtime/langchain/interfaces/llm_processor.py +4 -2
- alita_sdk/runtime/langchain/langraph_agent.py +407 -92
- alita_sdk/runtime/langchain/utils.py +102 -8
- alita_sdk/runtime/llms/preloaded.py +2 -6
- alita_sdk/runtime/models/mcp_models.py +61 -0
- alita_sdk/runtime/skills/__init__.py +91 -0
- alita_sdk/runtime/skills/callbacks.py +498 -0
- alita_sdk/runtime/skills/discovery.py +540 -0
- alita_sdk/runtime/skills/executor.py +610 -0
- alita_sdk/runtime/skills/input_builder.py +371 -0
- alita_sdk/runtime/skills/models.py +330 -0
- alita_sdk/runtime/skills/registry.py +355 -0
- alita_sdk/runtime/skills/skill_runner.py +330 -0
- alita_sdk/runtime/toolkits/__init__.py +28 -0
- alita_sdk/runtime/toolkits/application.py +14 -4
- alita_sdk/runtime/toolkits/artifact.py +24 -9
- alita_sdk/runtime/toolkits/datasource.py +13 -6
- alita_sdk/runtime/toolkits/mcp.py +780 -0
- alita_sdk/runtime/toolkits/planning.py +178 -0
- alita_sdk/runtime/toolkits/skill_router.py +238 -0
- alita_sdk/runtime/toolkits/subgraph.py +11 -6
- alita_sdk/runtime/toolkits/tools.py +314 -70
- alita_sdk/runtime/toolkits/vectorstore.py +11 -5
- alita_sdk/runtime/tools/__init__.py +24 -0
- alita_sdk/runtime/tools/application.py +16 -4
- alita_sdk/runtime/tools/artifact.py +367 -33
- alita_sdk/runtime/tools/data_analysis.py +183 -0
- alita_sdk/runtime/tools/function.py +100 -4
- alita_sdk/runtime/tools/graph.py +81 -0
- alita_sdk/runtime/tools/image_generation.py +218 -0
- alita_sdk/runtime/tools/llm.py +1013 -177
- alita_sdk/runtime/tools/loop.py +3 -1
- alita_sdk/runtime/tools/loop_output.py +3 -1
- alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
- alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
- alita_sdk/runtime/tools/mcp_server_tool.py +3 -1
- alita_sdk/runtime/tools/planning/__init__.py +36 -0
- alita_sdk/runtime/tools/planning/models.py +246 -0
- alita_sdk/runtime/tools/planning/wrapper.py +607 -0
- alita_sdk/runtime/tools/router.py +2 -1
- alita_sdk/runtime/tools/sandbox.py +375 -0
- alita_sdk/runtime/tools/skill_router.py +776 -0
- alita_sdk/runtime/tools/tool.py +3 -1
- alita_sdk/runtime/tools/vectorstore.py +69 -65
- alita_sdk/runtime/tools/vectorstore_base.py +163 -90
- alita_sdk/runtime/utils/AlitaCallback.py +137 -21
- alita_sdk/runtime/utils/mcp_client.py +492 -0
- alita_sdk/runtime/utils/mcp_oauth.py +361 -0
- alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
- alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
- alita_sdk/runtime/utils/streamlit.py +41 -14
- alita_sdk/runtime/utils/toolkit_utils.py +28 -9
- alita_sdk/runtime/utils/utils.py +48 -0
- alita_sdk/tools/__init__.py +135 -37
- alita_sdk/tools/ado/__init__.py +2 -2
- alita_sdk/tools/ado/repos/__init__.py +15 -19
- alita_sdk/tools/ado/repos/repos_wrapper.py +12 -20
- alita_sdk/tools/ado/test_plan/__init__.py +26 -8
- alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -28
- alita_sdk/tools/ado/wiki/__init__.py +27 -12
- alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -40
- alita_sdk/tools/ado/work_item/__init__.py +27 -12
- alita_sdk/tools/ado/work_item/ado_wrapper.py +95 -11
- alita_sdk/tools/advanced_jira_mining/__init__.py +12 -8
- alita_sdk/tools/aws/delta_lake/__init__.py +14 -11
- alita_sdk/tools/aws/delta_lake/tool.py +5 -1
- alita_sdk/tools/azure_ai/search/__init__.py +13 -8
- alita_sdk/tools/base/tool.py +5 -1
- alita_sdk/tools/base_indexer_toolkit.py +454 -110
- alita_sdk/tools/bitbucket/__init__.py +27 -19
- alita_sdk/tools/bitbucket/api_wrapper.py +285 -27
- alita_sdk/tools/bitbucket/cloud_api_wrapper.py +5 -5
- alita_sdk/tools/browser/__init__.py +41 -16
- alita_sdk/tools/browser/crawler.py +3 -1
- alita_sdk/tools/browser/utils.py +15 -6
- alita_sdk/tools/carrier/__init__.py +18 -17
- alita_sdk/tools/carrier/backend_reports_tool.py +8 -4
- alita_sdk/tools/carrier/excel_reporter.py +8 -4
- alita_sdk/tools/chunkers/__init__.py +3 -1
- alita_sdk/tools/chunkers/code/codeparser.py +1 -1
- alita_sdk/tools/chunkers/sematic/json_chunker.py +2 -1
- alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
- alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
- alita_sdk/tools/chunkers/universal_chunker.py +270 -0
- alita_sdk/tools/cloud/aws/__init__.py +11 -7
- alita_sdk/tools/cloud/azure/__init__.py +11 -7
- alita_sdk/tools/cloud/gcp/__init__.py +11 -7
- alita_sdk/tools/cloud/k8s/__init__.py +11 -7
- alita_sdk/tools/code/linter/__init__.py +9 -8
- alita_sdk/tools/code/loaders/codesearcher.py +3 -2
- alita_sdk/tools/code/sonar/__init__.py +20 -13
- alita_sdk/tools/code_indexer_toolkit.py +199 -0
- alita_sdk/tools/confluence/__init__.py +21 -14
- alita_sdk/tools/confluence/api_wrapper.py +197 -58
- alita_sdk/tools/confluence/loader.py +14 -2
- alita_sdk/tools/custom_open_api/__init__.py +11 -5
- alita_sdk/tools/elastic/__init__.py +10 -8
- alita_sdk/tools/elitea_base.py +546 -64
- alita_sdk/tools/figma/__init__.py +11 -8
- alita_sdk/tools/figma/api_wrapper.py +352 -153
- alita_sdk/tools/github/__init__.py +17 -17
- alita_sdk/tools/github/api_wrapper.py +9 -26
- alita_sdk/tools/github/github_client.py +81 -12
- alita_sdk/tools/github/schemas.py +2 -1
- alita_sdk/tools/github/tool.py +5 -1
- alita_sdk/tools/gitlab/__init__.py +18 -13
- alita_sdk/tools/gitlab/api_wrapper.py +224 -80
- alita_sdk/tools/gitlab_org/__init__.py +13 -10
- alita_sdk/tools/google/bigquery/__init__.py +13 -13
- alita_sdk/tools/google/bigquery/tool.py +5 -1
- alita_sdk/tools/google_places/__init__.py +20 -11
- alita_sdk/tools/jira/__init__.py +21 -11
- alita_sdk/tools/jira/api_wrapper.py +315 -168
- alita_sdk/tools/keycloak/__init__.py +10 -8
- alita_sdk/tools/localgit/__init__.py +8 -3
- alita_sdk/tools/localgit/local_git.py +62 -54
- alita_sdk/tools/localgit/tool.py +5 -1
- alita_sdk/tools/memory/__init__.py +38 -14
- alita_sdk/tools/non_code_indexer_toolkit.py +7 -2
- alita_sdk/tools/ocr/__init__.py +10 -8
- alita_sdk/tools/openapi/__init__.py +281 -108
- alita_sdk/tools/openapi/api_wrapper.py +883 -0
- alita_sdk/tools/openapi/tool.py +20 -0
- alita_sdk/tools/pandas/__init__.py +18 -11
- alita_sdk/tools/pandas/api_wrapper.py +40 -45
- alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
- alita_sdk/tools/postman/__init__.py +10 -11
- alita_sdk/tools/postman/api_wrapper.py +19 -8
- alita_sdk/tools/postman/postman_analysis.py +8 -1
- alita_sdk/tools/pptx/__init__.py +10 -10
- alita_sdk/tools/qtest/__init__.py +21 -14
- alita_sdk/tools/qtest/api_wrapper.py +1784 -88
- alita_sdk/tools/rally/__init__.py +12 -10
- alita_sdk/tools/report_portal/__init__.py +22 -16
- alita_sdk/tools/salesforce/__init__.py +21 -16
- alita_sdk/tools/servicenow/__init__.py +20 -16
- alita_sdk/tools/servicenow/api_wrapper.py +1 -1
- alita_sdk/tools/sharepoint/__init__.py +16 -14
- alita_sdk/tools/sharepoint/api_wrapper.py +179 -39
- alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
- alita_sdk/tools/sharepoint/utils.py +8 -2
- alita_sdk/tools/slack/__init__.py +11 -7
- alita_sdk/tools/sql/__init__.py +21 -19
- alita_sdk/tools/sql/api_wrapper.py +71 -23
- alita_sdk/tools/testio/__init__.py +20 -13
- alita_sdk/tools/testrail/__init__.py +12 -11
- alita_sdk/tools/testrail/api_wrapper.py +214 -46
- alita_sdk/tools/utils/__init__.py +28 -4
- alita_sdk/tools/utils/content_parser.py +182 -62
- alita_sdk/tools/utils/text_operations.py +254 -0
- alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
- alita_sdk/tools/xray/__init__.py +17 -14
- alita_sdk/tools/xray/api_wrapper.py +58 -113
- alita_sdk/tools/yagmail/__init__.py +8 -3
- alita_sdk/tools/zephyr/__init__.py +11 -7
- alita_sdk/tools/zephyr_enterprise/__init__.py +15 -9
- alita_sdk/tools/zephyr_enterprise/api_wrapper.py +30 -15
- alita_sdk/tools/zephyr_essential/__init__.py +15 -10
- alita_sdk/tools/zephyr_essential/api_wrapper.py +297 -54
- alita_sdk/tools/zephyr_essential/client.py +6 -4
- alita_sdk/tools/zephyr_scale/__init__.py +12 -8
- alita_sdk/tools/zephyr_scale/api_wrapper.py +39 -31
- alita_sdk/tools/zephyr_squad/__init__.py +11 -7
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/METADATA +184 -37
- alita_sdk-0.3.562.dist-info/RECORD +450 -0
- alita_sdk-0.3.562.dist-info/entry_points.txt +2 -0
- alita_sdk/tools/bitbucket/tools.py +0 -304
- alita_sdk-0.3.257.dist-info/RECORD +0 -343
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/licenses/LICENSE +0 -0
- {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")
|
alita_sdk/configurations/jira.py
CHANGED
|
@@ -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
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
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
|
|
20
|
-
qtest_api_token:
|
|
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": ["
|
|
14
|
+
"categories": ["other"],
|
|
26
15
|
"extra_categories": ["servicenow", "itsm", "service management", "incident"],
|
|
27
16
|
}
|
|
28
17
|
}
|