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.
- 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 +3794 -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 +323 -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 +493 -105
- alita_sdk/runtime/langchain/utils.py +118 -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 +25 -9
- alita_sdk/runtime/toolkits/datasource.py +13 -6
- alita_sdk/runtime/toolkits/mcp.py +782 -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 +1032 -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/constants.py +5 -1
- 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 +16 -19
- alita_sdk/tools/ado/repos/repos_wrapper.py +12 -20
- alita_sdk/tools/ado/test_plan/__init__.py +27 -8
- alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -28
- alita_sdk/tools/ado/wiki/__init__.py +28 -12
- alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -40
- alita_sdk/tools/ado/work_item/__init__.py +28 -12
- alita_sdk/tools/ado/work_item/ado_wrapper.py +95 -11
- alita_sdk/tools/advanced_jira_mining/__init__.py +13 -8
- alita_sdk/tools/aws/delta_lake/__init__.py +15 -11
- alita_sdk/tools/aws/delta_lake/tool.py +5 -1
- alita_sdk/tools/azure_ai/search/__init__.py +14 -8
- alita_sdk/tools/base/tool.py +5 -1
- alita_sdk/tools/base_indexer_toolkit.py +454 -110
- alita_sdk/tools/bitbucket/__init__.py +28 -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 +12 -7
- alita_sdk/tools/cloud/azure/__init__.py +12 -7
- alita_sdk/tools/cloud/gcp/__init__.py +12 -7
- alita_sdk/tools/cloud/k8s/__init__.py +12 -7
- alita_sdk/tools/code/linter/__init__.py +10 -8
- alita_sdk/tools/code/loaders/codesearcher.py +3 -2
- alita_sdk/tools/code/sonar/__init__.py +21 -13
- alita_sdk/tools/code_indexer_toolkit.py +199 -0
- alita_sdk/tools/confluence/__init__.py +22 -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 +12 -5
- alita_sdk/tools/elastic/__init__.py +11 -8
- alita_sdk/tools/elitea_base.py +546 -64
- alita_sdk/tools/figma/__init__.py +60 -11
- alita_sdk/tools/figma/api_wrapper.py +1400 -167
- alita_sdk/tools/figma/figma_client.py +73 -0
- alita_sdk/tools/figma/toon_tools.py +2748 -0
- alita_sdk/tools/github/__init__.py +18 -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 +19 -13
- alita_sdk/tools/gitlab/api_wrapper.py +256 -80
- alita_sdk/tools/gitlab_org/__init__.py +14 -10
- alita_sdk/tools/google/bigquery/__init__.py +14 -13
- alita_sdk/tools/google/bigquery/tool.py +5 -1
- alita_sdk/tools/google_places/__init__.py +21 -11
- alita_sdk/tools/jira/__init__.py +22 -11
- alita_sdk/tools/jira/api_wrapper.py +315 -168
- alita_sdk/tools/keycloak/__init__.py +11 -8
- alita_sdk/tools/localgit/__init__.py +9 -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 +11 -8
- alita_sdk/tools/openapi/__init__.py +491 -106
- alita_sdk/tools/openapi/api_wrapper.py +1357 -0
- alita_sdk/tools/openapi/tool.py +20 -0
- alita_sdk/tools/pandas/__init__.py +20 -12
- 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 +11 -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 +11 -10
- alita_sdk/tools/qtest/__init__.py +22 -14
- alita_sdk/tools/qtest/api_wrapper.py +1784 -88
- alita_sdk/tools/rally/__init__.py +13 -10
- alita_sdk/tools/report_portal/__init__.py +23 -16
- alita_sdk/tools/salesforce/__init__.py +22 -16
- alita_sdk/tools/servicenow/__init__.py +21 -16
- alita_sdk/tools/servicenow/api_wrapper.py +1 -1
- alita_sdk/tools/sharepoint/__init__.py +17 -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 +13 -8
- alita_sdk/tools/sql/__init__.py +22 -19
- alita_sdk/tools/sql/api_wrapper.py +71 -23
- alita_sdk/tools/testio/__init__.py +21 -13
- alita_sdk/tools/testrail/__init__.py +13 -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 +241 -55
- alita_sdk/tools/utils/text_operations.py +254 -0
- alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
- alita_sdk/tools/xray/__init__.py +18 -14
- alita_sdk/tools/xray/api_wrapper.py +58 -113
- alita_sdk/tools/yagmail/__init__.py +9 -3
- alita_sdk/tools/zephyr/__init__.py +12 -7
- alita_sdk/tools/zephyr_enterprise/__init__.py +16 -9
- alita_sdk/tools/zephyr_enterprise/api_wrapper.py +30 -15
- alita_sdk/tools/zephyr_essential/__init__.py +16 -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 +13 -8
- alita_sdk/tools/zephyr_scale/api_wrapper.py +39 -31
- alita_sdk/tools/zephyr_squad/__init__.py +12 -7
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/METADATA +184 -37
- alita_sdk-0.3.584.dist-info/RECORD +452 -0
- alita_sdk-0.3.584.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.584.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.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,323 @@
|
|
|
1
|
+
from typing import Any, Literal, Optional
|
|
2
|
+
from pydantic import BaseModel, ConfigDict, Field, SecretStr, model_validator
|
|
3
|
+
|
|
4
|
+
import base64
|
|
5
|
+
import requests
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class OpenApiConfiguration(BaseModel):
|
|
9
|
+
"""
|
|
10
|
+
OpenAPI configuration for authentication.
|
|
11
|
+
|
|
12
|
+
Supports three authentication modes:
|
|
13
|
+
- Anonymous: No authentication (all fields empty)
|
|
14
|
+
- API Key: Static key sent via header (Bearer, Basic, or Custom)
|
|
15
|
+
- OAuth2 Client Credentials: Machine-to-machine authentication flow
|
|
16
|
+
|
|
17
|
+
Note: Only OAuth2 Client Credentials flow is supported. Authorization Code flow
|
|
18
|
+
is not supported as it requires user interaction and pre-registered redirect URLs.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
model_config = ConfigDict(
|
|
22
|
+
extra='allow',
|
|
23
|
+
json_schema_extra={
|
|
24
|
+
"metadata": {
|
|
25
|
+
"label": "OpenAPI",
|
|
26
|
+
"icon_url": "openapi.svg",
|
|
27
|
+
"categories": ["integrations"],
|
|
28
|
+
"type": "openapi",
|
|
29
|
+
"extra_categories": ["api", "openapi", "swagger"],
|
|
30
|
+
"sections": {
|
|
31
|
+
"auth": {
|
|
32
|
+
"required": False,
|
|
33
|
+
"subsections": [
|
|
34
|
+
{
|
|
35
|
+
"name": "API Key",
|
|
36
|
+
"fields": ["api_key", "auth_type", "custom_header_name"],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "OAuth",
|
|
40
|
+
"fields": [
|
|
41
|
+
"client_id",
|
|
42
|
+
"client_secret",
|
|
43
|
+
"token_url",
|
|
44
|
+
"scope",
|
|
45
|
+
"method",
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
"section": "credentials",
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# =========================================================================
|
|
57
|
+
# API Key Authentication Fields
|
|
58
|
+
# =========================================================================
|
|
59
|
+
|
|
60
|
+
api_key: Optional[SecretStr] = Field(
|
|
61
|
+
default=None,
|
|
62
|
+
description=(
|
|
63
|
+
"API key value (stored as a secret). Used when selecting 'API Key' authentication subsection."
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
auth_type: Optional[Literal['Basic', 'Bearer', 'Custom']] = Field(
|
|
67
|
+
default='Bearer',
|
|
68
|
+
description=(
|
|
69
|
+
"How to apply the API key. "
|
|
70
|
+
"- 'Bearer': sets 'Authorization: Bearer <api_key>' "
|
|
71
|
+
"- 'Basic': sets 'Authorization: Basic <api_key>' "
|
|
72
|
+
"- 'custom': sets '<custom_header_name>: <api_key>'"
|
|
73
|
+
),
|
|
74
|
+
)
|
|
75
|
+
custom_header_name: Optional[str] = Field(
|
|
76
|
+
default=None,
|
|
77
|
+
description="Custom header name to use when auth_type='custom' (e.g. 'X-Api-Key').",
|
|
78
|
+
json_schema_extra={'visible_when': {'field': 'auth_type', 'value': 'custom'}},
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# =========================================================================
|
|
82
|
+
# OAuth2 Client Credentials Flow Fields
|
|
83
|
+
# =========================================================================
|
|
84
|
+
|
|
85
|
+
client_id: Optional[str] = Field(
|
|
86
|
+
default=None,
|
|
87
|
+
description='OAuth2 client ID (also known as Application ID or App ID)'
|
|
88
|
+
)
|
|
89
|
+
client_secret: Optional[SecretStr] = Field(
|
|
90
|
+
default=None,
|
|
91
|
+
description='OAuth2 client secret (stored securely)'
|
|
92
|
+
)
|
|
93
|
+
token_url: Optional[str] = Field(
|
|
94
|
+
default=None,
|
|
95
|
+
description=(
|
|
96
|
+
'OAuth2 token endpoint URL for obtaining access tokens. '
|
|
97
|
+
'Examples: '
|
|
98
|
+
'Azure AD: https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token, '
|
|
99
|
+
'Google: https://oauth2.googleapis.com/token, '
|
|
100
|
+
'Auth0: https://{domain}/oauth/token, '
|
|
101
|
+
'Spotify: https://accounts.spotify.com/api/token'
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
scope: Optional[str] = Field(
|
|
105
|
+
default=None,
|
|
106
|
+
description=(
|
|
107
|
+
'OAuth2 scope(s), space-separated if multiple (per OAuth2 RFC 6749). '
|
|
108
|
+
'Examples: "user-read-private user-read-email" (Spotify), '
|
|
109
|
+
'"api://app-id/.default" (Azure), '
|
|
110
|
+
'"https://www.googleapis.com/auth/cloud-platform" (Google)'
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
method: Optional[Literal['default', 'Basic']] = Field(
|
|
114
|
+
default='default',
|
|
115
|
+
description=(
|
|
116
|
+
"Token exchange method for client credentials flow. "
|
|
117
|
+
"'default': Sends client_id and client_secret in POST body (Azure AD, Auth0, most providers). "
|
|
118
|
+
"'Basic': Sends credentials via HTTP Basic auth header - required by Spotify, some AWS services, and certain OAuth providers."
|
|
119
|
+
),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
@model_validator(mode='before')
|
|
123
|
+
@classmethod
|
|
124
|
+
def _validate_auth_consistency(cls, values):
|
|
125
|
+
if not isinstance(values, dict):
|
|
126
|
+
return values
|
|
127
|
+
|
|
128
|
+
# OAuth: if any OAuth field is provided, require the essential ones
|
|
129
|
+
has_any_oauth = any(
|
|
130
|
+
(values.get('client_id'), values.get('client_secret'), values.get('token_url'))
|
|
131
|
+
)
|
|
132
|
+
if has_any_oauth:
|
|
133
|
+
missing = []
|
|
134
|
+
if not values.get('client_id'):
|
|
135
|
+
missing.append('client_id')
|
|
136
|
+
if not values.get('client_secret'):
|
|
137
|
+
missing.append('client_secret')
|
|
138
|
+
if not values.get('token_url'):
|
|
139
|
+
missing.append('token_url')
|
|
140
|
+
if missing:
|
|
141
|
+
raise ValueError(f"OAuth is misconfigured; missing: {', '.join(missing)}")
|
|
142
|
+
|
|
143
|
+
# API key: if auth_type is custom, custom_header_name must be present
|
|
144
|
+
auth_type = values.get('auth_type')
|
|
145
|
+
if isinstance(auth_type, str) and auth_type.strip().lower() == 'custom' and values.get('api_key'):
|
|
146
|
+
if not values.get('custom_header_name'):
|
|
147
|
+
raise ValueError("custom_header_name is required when auth_type='custom'")
|
|
148
|
+
|
|
149
|
+
return values
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
def check_connection(settings: dict) -> str | None:
|
|
153
|
+
"""
|
|
154
|
+
Validate the OpenAPI configuration by testing connectivity where possible.
|
|
155
|
+
|
|
156
|
+
Validation behavior by authentication type:
|
|
157
|
+
|
|
158
|
+
1. ANONYMOUS (no auth fields configured):
|
|
159
|
+
- Cannot validate without making actual API calls
|
|
160
|
+
- Returns None (success) - validation skipped
|
|
161
|
+
|
|
162
|
+
2. API KEY (api_key field configured):
|
|
163
|
+
- Cannot validate without knowing which endpoint to call
|
|
164
|
+
- The OpenAPI spec is not available at configuration time
|
|
165
|
+
- Returns None (success) - validation skipped
|
|
166
|
+
|
|
167
|
+
3. OAUTH2 CLIENT CREDENTIALS (client_id, client_secret, token_url configured):
|
|
168
|
+
- CAN validate by attempting token exchange with the OAuth provider
|
|
169
|
+
- Makes a real HTTP request to token_url
|
|
170
|
+
- Returns None on success, error message on failure
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
settings: Dictionary containing OpenAPI configuration fields
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
None: Configuration is valid (or cannot be validated for this auth type)
|
|
177
|
+
str: Error message describing the validation failure
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
# =====================================================================
|
|
181
|
+
# Determine authentication type from configured fields
|
|
182
|
+
# =====================================================================
|
|
183
|
+
|
|
184
|
+
client_id = settings.get('client_id')
|
|
185
|
+
client_secret = settings.get('client_secret')
|
|
186
|
+
token_url = settings.get('token_url')
|
|
187
|
+
|
|
188
|
+
has_oauth_fields = client_id or client_secret or token_url
|
|
189
|
+
|
|
190
|
+
# =====================================================================
|
|
191
|
+
# ANONYMOUS or API KEY: Cannot validate, return success
|
|
192
|
+
# =====================================================================
|
|
193
|
+
|
|
194
|
+
if not has_oauth_fields:
|
|
195
|
+
# No OAuth fields configured - this is either:
|
|
196
|
+
# - Anonymous authentication (no auth at all)
|
|
197
|
+
# - API Key authentication (api_key field may be set)
|
|
198
|
+
#
|
|
199
|
+
# Neither can be validated without making actual API calls to the
|
|
200
|
+
# target service, and we don't have the OpenAPI spec available here.
|
|
201
|
+
return None
|
|
202
|
+
|
|
203
|
+
# =====================================================================
|
|
204
|
+
# OAUTH2: Validate by attempting token exchange
|
|
205
|
+
# =====================================================================
|
|
206
|
+
|
|
207
|
+
# Check for required OAuth fields
|
|
208
|
+
if not client_id:
|
|
209
|
+
return "OAuth client_id is required when using OAuth authentication"
|
|
210
|
+
if not client_secret:
|
|
211
|
+
return "OAuth client_secret is required when using OAuth authentication"
|
|
212
|
+
if not token_url:
|
|
213
|
+
return "OAuth token_url is required when using OAuth authentication"
|
|
214
|
+
|
|
215
|
+
# Extract secret value if it's a SecretStr
|
|
216
|
+
if hasattr(client_secret, 'get_secret_value'):
|
|
217
|
+
client_secret = client_secret.get_secret_value()
|
|
218
|
+
|
|
219
|
+
if not client_secret or not str(client_secret).strip():
|
|
220
|
+
return "OAuth client_secret cannot be empty"
|
|
221
|
+
|
|
222
|
+
# Validate token_url format
|
|
223
|
+
token_url = token_url.strip()
|
|
224
|
+
if not token_url.startswith(('http://', 'https://')):
|
|
225
|
+
return "OAuth token_url must start with http:// or https://"
|
|
226
|
+
|
|
227
|
+
# Get optional OAuth settings
|
|
228
|
+
scope = settings.get('scope')
|
|
229
|
+
method = settings.get('method', 'default') or 'default'
|
|
230
|
+
|
|
231
|
+
# ---------------------------------------------------------------------
|
|
232
|
+
# Attempt OAuth2 Client Credentials token exchange
|
|
233
|
+
# ---------------------------------------------------------------------
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
headers = {
|
|
237
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
238
|
+
'Accept': 'application/json',
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
data = {
|
|
242
|
+
'grant_type': 'client_credentials',
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
# Apply credentials based on method
|
|
246
|
+
if method == 'Basic':
|
|
247
|
+
# Basic method: credentials in Authorization header (Spotify, some AWS)
|
|
248
|
+
credentials = f"{client_id}:{client_secret}"
|
|
249
|
+
encoded = base64.b64encode(credentials.encode('utf-8')).decode('utf-8')
|
|
250
|
+
headers['Authorization'] = f'Basic {encoded}'
|
|
251
|
+
else:
|
|
252
|
+
# Default method: credentials in POST body (Azure AD, Auth0, most providers)
|
|
253
|
+
data['client_id'] = client_id
|
|
254
|
+
data['client_secret'] = str(client_secret)
|
|
255
|
+
|
|
256
|
+
if scope:
|
|
257
|
+
data['scope'] = scope
|
|
258
|
+
|
|
259
|
+
response = requests.post(
|
|
260
|
+
token_url,
|
|
261
|
+
headers=headers,
|
|
262
|
+
data=data,
|
|
263
|
+
timeout=30,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
# ---------------------------------------------------------------------
|
|
267
|
+
# Handle response
|
|
268
|
+
# ---------------------------------------------------------------------
|
|
269
|
+
|
|
270
|
+
if response.status_code == 200:
|
|
271
|
+
try:
|
|
272
|
+
token_data = response.json()
|
|
273
|
+
if 'access_token' in token_data:
|
|
274
|
+
return None # Success - token obtained
|
|
275
|
+
return "OAuth response did not contain 'access_token'"
|
|
276
|
+
except Exception:
|
|
277
|
+
return "Failed to parse OAuth token response"
|
|
278
|
+
|
|
279
|
+
# Handle common error status codes with helpful messages
|
|
280
|
+
if response.status_code == 400:
|
|
281
|
+
try:
|
|
282
|
+
error_data = response.json()
|
|
283
|
+
error = error_data.get('error', 'bad_request')
|
|
284
|
+
error_desc = error_data.get('error_description', '')
|
|
285
|
+
if error_desc:
|
|
286
|
+
return f"OAuth error: {error} - {error_desc}"
|
|
287
|
+
return f"OAuth error: {error}"
|
|
288
|
+
except Exception:
|
|
289
|
+
return "OAuth request failed: bad request (400)"
|
|
290
|
+
|
|
291
|
+
if response.status_code == 401:
|
|
292
|
+
return "OAuth authentication failed: invalid client_id or client_secret"
|
|
293
|
+
|
|
294
|
+
if response.status_code == 403:
|
|
295
|
+
return "OAuth access forbidden: client may lack required permissions"
|
|
296
|
+
|
|
297
|
+
if response.status_code == 404:
|
|
298
|
+
return f"OAuth token endpoint not found: {token_url}"
|
|
299
|
+
|
|
300
|
+
return f"OAuth token request failed with status {response.status_code}"
|
|
301
|
+
|
|
302
|
+
except requests.exceptions.SSLError as e:
|
|
303
|
+
error_str = str(e).lower()
|
|
304
|
+
if 'hostname mismatch' in error_str:
|
|
305
|
+
return "OAuth token_url hostname does not match SSL certificate - verify the URL is correct"
|
|
306
|
+
if 'certificate verify failed' in error_str:
|
|
307
|
+
return "SSL certificate verification failed for OAuth endpoint - the server may have an invalid or self-signed certificate"
|
|
308
|
+
if 'certificate has expired' in error_str:
|
|
309
|
+
return "SSL certificate has expired for OAuth endpoint"
|
|
310
|
+
return "SSL error connecting to OAuth endpoint - verify the token_url is correct"
|
|
311
|
+
except requests.exceptions.ConnectionError as e:
|
|
312
|
+
error_str = str(e).lower()
|
|
313
|
+
if 'name or service not known' in error_str or 'nodename nor servname provided' in error_str:
|
|
314
|
+
return "OAuth token_url hostname could not be resolved - verify the URL is correct"
|
|
315
|
+
if 'connection refused' in error_str:
|
|
316
|
+
return "Connection refused by OAuth endpoint - verify the token_url and port are correct"
|
|
317
|
+
return "Cannot connect to OAuth token endpoint - verify the token_url is correct"
|
|
318
|
+
except requests.exceptions.Timeout:
|
|
319
|
+
return "OAuth token request timed out - the endpoint may be unreachable"
|
|
320
|
+
except requests.exceptions.RequestException:
|
|
321
|
+
return "OAuth request failed - verify the token_url is correct and accessible"
|
|
322
|
+
except Exception:
|
|
323
|
+
return "Unexpected error during OAuth configuration validation"
|