alita-sdk 0.3.162__py3-none-any.whl → 0.3.163__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/tools/__init__.py +1 -0
- alita_sdk/tools/carrier/api_wrapper.py +74 -2
- alita_sdk/tools/carrier/cancel_ui_test_tool.py +178 -0
- alita_sdk/tools/carrier/carrier_sdk.py +71 -3
- alita_sdk/tools/carrier/create_ui_excel_report_tool.py +473 -0
- alita_sdk/tools/carrier/create_ui_test_tool.py +199 -0
- alita_sdk/tools/carrier/lighthouse_excel_reporter.py +155 -0
- alita_sdk/tools/carrier/run_ui_test_tool.py +394 -0
- alita_sdk/tools/carrier/tools.py +11 -1
- alita_sdk/tools/carrier/ui_reports_tool.py +6 -2
- alita_sdk/tools/carrier/update_ui_test_schedule_tool.py +278 -0
- alita_sdk/tools/zephyr_squad/__init__.py +62 -0
- alita_sdk/tools/zephyr_squad/api_wrapper.py +135 -0
- alita_sdk/tools/zephyr_squad/zephyr_squad_cloud_client.py +79 -0
- {alita_sdk-0.3.162.dist-info → alita_sdk-0.3.163.dist-info}/METADATA +1 -1
- {alita_sdk-0.3.162.dist-info → alita_sdk-0.3.163.dist-info}/RECORD +19 -10
- {alita_sdk-0.3.162.dist-info → alita_sdk-0.3.163.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.162.dist-info → alita_sdk-0.3.163.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.162.dist-info → alita_sdk-0.3.163.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,278 @@
|
|
1
|
+
import logging
|
2
|
+
import json
|
3
|
+
import traceback
|
4
|
+
import re
|
5
|
+
from typing import Type
|
6
|
+
from langchain_core.tools import BaseTool, ToolException
|
7
|
+
from pydantic.fields import Field
|
8
|
+
from pydantic import create_model, BaseModel
|
9
|
+
from .api_wrapper import CarrierAPIWrapper
|
10
|
+
|
11
|
+
|
12
|
+
logger = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
class UpdateUITestScheduleTool(BaseTool):
|
16
|
+
api_wrapper: CarrierAPIWrapper = Field(..., description="Carrier API Wrapper instance")
|
17
|
+
name: str = "update_ui_test_schedule"
|
18
|
+
description: str = ("Update UI test schedule on the Carrier platform. Use this tool when user wants to update, modify, or change a UI test schedule. "
|
19
|
+
"Provide test_id, schedule_name, and cron_timer, or leave empty to see available tests.")
|
20
|
+
args_schema: Type[BaseModel] = create_model(
|
21
|
+
"UpdateUITestScheduleInput",
|
22
|
+
test_id=(str, Field(default="", description="Test ID to update schedule for")),
|
23
|
+
schedule_name=(str, Field(default="", description="Name for the new schedule")),
|
24
|
+
cron_timer=(str, Field(default="", description="Cron expression for schedule timing (e.g., '0 2 * * *')")),
|
25
|
+
)
|
26
|
+
|
27
|
+
def _run(self, test_id: str = "", schedule_name: str = "", cron_timer: str = ""):
|
28
|
+
try:
|
29
|
+
# Check if no parameters provided - show available tests
|
30
|
+
if (not test_id or test_id.strip() == "") and (not schedule_name or schedule_name.strip() == "") and (not cron_timer or cron_timer.strip() == ""):
|
31
|
+
return self._show_available_tests_and_instructions()
|
32
|
+
|
33
|
+
# Check if test_id is missing but other params provided
|
34
|
+
if (not test_id or test_id.strip() == ""):
|
35
|
+
return self._show_missing_test_id_message()
|
36
|
+
|
37
|
+
# Check if schedule_name or cron_timer is missing
|
38
|
+
if (not schedule_name or schedule_name.strip() == "") or (not cron_timer or cron_timer.strip() == ""):
|
39
|
+
return self._show_missing_parameters_message(test_id, schedule_name, cron_timer)
|
40
|
+
|
41
|
+
# Validate cron timer format
|
42
|
+
if not self._validate_cron_timer(cron_timer):
|
43
|
+
return self._show_invalid_cron_message(cron_timer)
|
44
|
+
|
45
|
+
# Get UI tests list to verify test exists
|
46
|
+
ui_tests = self.api_wrapper.get_ui_tests_list()
|
47
|
+
test_data = None
|
48
|
+
test_id_int = None
|
49
|
+
|
50
|
+
# Try to find test by ID
|
51
|
+
if test_id.isdigit():
|
52
|
+
test_id_int = int(test_id)
|
53
|
+
for test in ui_tests:
|
54
|
+
if test.get("id") == test_id_int:
|
55
|
+
test_data = test
|
56
|
+
break
|
57
|
+
|
58
|
+
if not test_data:
|
59
|
+
available_tests = []
|
60
|
+
for test in ui_tests:
|
61
|
+
available_tests.append(f"ID: {test.get('id')}, Name: {test.get('name')}")
|
62
|
+
|
63
|
+
return f"❌ **Test not found for ID: {test_id}**\n\n**Available UI tests:**\n" + "\n".join([f"- {test}" for test in available_tests])
|
64
|
+
|
65
|
+
# Get detailed test configuration
|
66
|
+
test_details = self.api_wrapper.get_ui_test_details(str(test_id_int))
|
67
|
+
|
68
|
+
if not test_details:
|
69
|
+
return f"❌ **Could not retrieve test details for test ID {test_id_int}.**"
|
70
|
+
|
71
|
+
# Parse and update the test configuration
|
72
|
+
updated_config = self._parse_and_update_test_data(test_details, schedule_name, cron_timer)
|
73
|
+
|
74
|
+
# Execute the PUT request to update the test
|
75
|
+
result = self.api_wrapper.update_ui_test(str(test_id_int), updated_config)
|
76
|
+
|
77
|
+
return self._format_success_message(test_data.get('name', 'Unknown'), test_id_int, schedule_name, cron_timer)
|
78
|
+
|
79
|
+
except Exception:
|
80
|
+
stacktrace = traceback.format_exc()
|
81
|
+
logger.error(f"Error updating UI test schedule: {stacktrace}")
|
82
|
+
raise ToolException(stacktrace)
|
83
|
+
|
84
|
+
def _show_available_tests_and_instructions(self):
|
85
|
+
"""Show available tests and instructions when no parameters provided."""
|
86
|
+
try:
|
87
|
+
ui_tests = self.api_wrapper.get_ui_tests_list()
|
88
|
+
|
89
|
+
if not ui_tests:
|
90
|
+
return "❌ **No UI tests found.**"
|
91
|
+
|
92
|
+
message = ["# 📋 Update UI Test Schedule\n"]
|
93
|
+
message.append("## Available UI Tests:")
|
94
|
+
|
95
|
+
for test in ui_tests:
|
96
|
+
message.append(f"- **ID: {test.get('id')}**, Name: `{test.get('name')}`, Runner: `{test.get('runner')}`")
|
97
|
+
|
98
|
+
message.append("\n## 📝 Instructions:")
|
99
|
+
message.append("For updating UI test schedule, please provide me:")
|
100
|
+
message.append("- **`test_id`** - The ID of the test you want to update")
|
101
|
+
message.append("- **`schedule_name`** - A name for your new schedule")
|
102
|
+
message.append("- **`cron_timer`** - Cron expression for timing (e.g., `0 2 * * *` for daily at 2 AM)")
|
103
|
+
|
104
|
+
message.append("\n## 💡 Example:")
|
105
|
+
message.append("```")
|
106
|
+
message.append("test_id: 42")
|
107
|
+
message.append("schedule_name: Daily Morning Test")
|
108
|
+
message.append("cron_timer: 0 2 * * *")
|
109
|
+
message.append("```")
|
110
|
+
|
111
|
+
return "\n".join(message)
|
112
|
+
|
113
|
+
except Exception:
|
114
|
+
stacktrace = traceback.format_exc()
|
115
|
+
logger.error(f"Error fetching UI tests list: {stacktrace}")
|
116
|
+
raise ToolException(stacktrace)
|
117
|
+
|
118
|
+
def _show_missing_test_id_message(self):
|
119
|
+
"""Show message when test_id is missing."""
|
120
|
+
return """# ❌ Missing Test ID
|
121
|
+
|
122
|
+
**For updating UI test schedule, please provide me:**
|
123
|
+
- **`test_id`** - The ID of the test you want to update
|
124
|
+
- **`schedule_name`** - A name for your new schedule
|
125
|
+
- **`cron_timer`** - Cron expression for timing
|
126
|
+
|
127
|
+
Use the tool without parameters to see available tests."""
|
128
|
+
|
129
|
+
def _show_missing_parameters_message(self, test_id: str, schedule_name: str, cron_timer: str):
|
130
|
+
"""Show message when some parameters are missing."""
|
131
|
+
missing = []
|
132
|
+
if not schedule_name or schedule_name.strip() == "":
|
133
|
+
missing.append("**`schedule_name`**")
|
134
|
+
if not cron_timer or cron_timer.strip() == "":
|
135
|
+
missing.append("**`cron_timer`**")
|
136
|
+
|
137
|
+
message = [f"# ❌ Missing Parameters for Test ID: {test_id}\n"]
|
138
|
+
message.append("**Missing parameters:**")
|
139
|
+
for param in missing:
|
140
|
+
message.append(f"- {param}")
|
141
|
+
|
142
|
+
message.append("\n**For updating UI test schedule, please provide:**")
|
143
|
+
message.append("- **`test_id`** ✅ (provided)")
|
144
|
+
message.append("- **`schedule_name`** - A name for your new schedule")
|
145
|
+
message.append("- **`cron_timer`** - Cron expression for timing (e.g., `0 2 * * *`)")
|
146
|
+
|
147
|
+
return "\n".join(message)
|
148
|
+
|
149
|
+
def _validate_cron_timer(self, cron_timer: str) -> bool:
|
150
|
+
"""Validate cron timer format."""
|
151
|
+
# Basic cron validation - should have 5 parts separated by spaces
|
152
|
+
parts = cron_timer.strip().split()
|
153
|
+
if len(parts) != 5:
|
154
|
+
return False
|
155
|
+
|
156
|
+
# Each part should contain only digits, *, /, -, or ,
|
157
|
+
cron_pattern = re.compile(r'^[0-9*,/-]+$')
|
158
|
+
return all(cron_pattern.match(part) for part in parts)
|
159
|
+
|
160
|
+
def _show_invalid_cron_message(self, cron_timer: str):
|
161
|
+
"""Show message for invalid cron timer."""
|
162
|
+
return f"""# ❌ Invalid Cron Timer Format
|
163
|
+
|
164
|
+
**Provided:** `{cron_timer}`
|
165
|
+
|
166
|
+
**Cron format should be:** `minute hour day month weekday`
|
167
|
+
|
168
|
+
## Valid Examples:
|
169
|
+
- `0 2 * * *` - Daily at 2:00 AM
|
170
|
+
- `30 14 * * 1` - Every Monday at 2:30 PM
|
171
|
+
- `0 */6 * * *` - Every 6 hours
|
172
|
+
- `15 10 1 * *` - First day of every month at 10:15 AM
|
173
|
+
- `0 9 * * 1-5` - Weekdays at 9:00 AM
|
174
|
+
|
175
|
+
## Format Rules:
|
176
|
+
- **Minute:** 0-59
|
177
|
+
- **Hour:** 0-23
|
178
|
+
- **Day:** 1-31
|
179
|
+
- **Month:** 1-12
|
180
|
+
- **Weekday:** 0-7 (0 and 7 are Sunday)
|
181
|
+
- Use **`*`** for "any value"
|
182
|
+
- Use **`,`** for multiple values
|
183
|
+
- Use **`-`** for ranges
|
184
|
+
- Use **`/`** for step values"""
|
185
|
+
|
186
|
+
def _parse_and_update_test_data(self, get_data: dict, schedule_name: str, cron_timer: str) -> dict:
|
187
|
+
"""Parse GET response data and transform it into the required format for PUT request."""
|
188
|
+
|
189
|
+
# Extract environment and test type from test parameters
|
190
|
+
env_type = ""
|
191
|
+
test_type = ""
|
192
|
+
for param in get_data.get("test_parameters", []):
|
193
|
+
if param.get("name") == "env_type":
|
194
|
+
env_type = param.get("default", "")
|
195
|
+
elif param.get("name") == "test_type":
|
196
|
+
test_type = param.get("default", "")
|
197
|
+
|
198
|
+
# Construct common_params from GET data
|
199
|
+
common_params = {
|
200
|
+
"aggregation": get_data.get("aggregation", "max"),
|
201
|
+
"cc_env_vars": get_data.get("cc_env_vars", {}),
|
202
|
+
"entrypoint": get_data.get("entrypoint", ""),
|
203
|
+
"env_type": env_type,
|
204
|
+
"env_vars": get_data.get("env_vars", {}),
|
205
|
+
"location": get_data.get("location", ""),
|
206
|
+
"loops": get_data.get("loops", 1),
|
207
|
+
"name": get_data.get("name", ""),
|
208
|
+
"parallel_runners": get_data.get("parallel_runners", 1),
|
209
|
+
"runner": get_data.get("runner", ""),
|
210
|
+
"source": get_data.get("source", {}),
|
211
|
+
"test_type": test_type
|
212
|
+
}
|
213
|
+
|
214
|
+
# Extract only required integrations (reporters and system)
|
215
|
+
integrations = {
|
216
|
+
"reporters": get_data.get("integrations", {}).get("reporters", {}),
|
217
|
+
"system": get_data.get("integrations", {}).get("system", {})
|
218
|
+
}
|
219
|
+
|
220
|
+
# Process existing schedules and add the new one
|
221
|
+
schedules = []
|
222
|
+
|
223
|
+
# Keep existing schedules
|
224
|
+
for schedule in get_data.get("schedules", []):
|
225
|
+
existing_schedule = {
|
226
|
+
"active": schedule.get("active", False),
|
227
|
+
"cron": schedule.get("cron", ""),
|
228
|
+
"cron_radio": "custom",
|
229
|
+
"errors": {},
|
230
|
+
"id": schedule.get("id"),
|
231
|
+
"name": schedule.get("name", ""),
|
232
|
+
"project_id": schedule.get("project_id"),
|
233
|
+
"rpc_kwargs": schedule.get("rpc_kwargs"),
|
234
|
+
"test_id": schedule.get("test_id"),
|
235
|
+
"test_params": schedule.get("test_params", [])
|
236
|
+
}
|
237
|
+
schedules.append(existing_schedule)
|
238
|
+
|
239
|
+
# Add the new schedule
|
240
|
+
new_schedule = {
|
241
|
+
"active": True,
|
242
|
+
"cron": cron_timer,
|
243
|
+
"cron_radio": "custom",
|
244
|
+
"errors": {},
|
245
|
+
"id": None, # New schedule, no ID yet
|
246
|
+
"name": schedule_name,
|
247
|
+
"test_params": []
|
248
|
+
}
|
249
|
+
schedules.append(new_schedule)
|
250
|
+
|
251
|
+
# Assemble the final PUT request data
|
252
|
+
put_data = {
|
253
|
+
"common_params": common_params,
|
254
|
+
"integrations": integrations,
|
255
|
+
"run_test": False,
|
256
|
+
"schedules": schedules,
|
257
|
+
"test_parameters": [] # Empty as required in PUT request
|
258
|
+
}
|
259
|
+
|
260
|
+
return put_data
|
261
|
+
|
262
|
+
def _format_success_message(self, test_name: str, test_id: int, schedule_name: str, cron_timer: str) -> str:
|
263
|
+
"""Format success message in markdown."""
|
264
|
+
return f"""# ✅ UI Test Schedule Updated Successfully!
|
265
|
+
|
266
|
+
## Test Information:
|
267
|
+
- **Test Name:** `{test_name}`
|
268
|
+
- **Test ID:** `{test_id}`
|
269
|
+
|
270
|
+
## New Schedule Added:
|
271
|
+
- **Schedule Name:** `{schedule_name}`
|
272
|
+
- **Cron Timer:** `{cron_timer}`
|
273
|
+
- **Status:** Active ✅
|
274
|
+
|
275
|
+
## 🎯 What happens next:
|
276
|
+
The test will now run automatically according to the specified schedule. You can view and manage schedules in the Carrier platform UI.
|
277
|
+
|
278
|
+
**Schedule will execute:** Based on cron expression `{cron_timer}`"""
|
@@ -0,0 +1,62 @@
|
|
1
|
+
from typing import List, Literal, Optional
|
2
|
+
|
3
|
+
from langchain_community.agent_toolkits.base import BaseToolkit
|
4
|
+
from langchain_core.tools import BaseTool
|
5
|
+
from pydantic import create_model, BaseModel, Field, SecretStr
|
6
|
+
|
7
|
+
from .api_wrapper import ZephyrSquadApiWrapper
|
8
|
+
from ..base.tool import BaseAction
|
9
|
+
from ..utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
|
10
|
+
|
11
|
+
name = "zephyr"
|
12
|
+
|
13
|
+
def get_tools(tool):
|
14
|
+
return ZephyrSquadToolkit().get_toolkit(
|
15
|
+
selected_tools=tool['settings'].get('selected_tools', []),
|
16
|
+
account_id=tool['settings']["account_id"],
|
17
|
+
access_key=tool['settings']["access_key"],
|
18
|
+
secret_key=tool['settings']["secret_key"],
|
19
|
+
toolkit_name=tool.get('toolkit_name')
|
20
|
+
).get_tools()
|
21
|
+
|
22
|
+
class ZephyrSquadToolkit(BaseToolkit):
|
23
|
+
tools: List[BaseTool] = []
|
24
|
+
toolkit_max_length: int = 0
|
25
|
+
|
26
|
+
@staticmethod
|
27
|
+
def toolkit_config_schema() -> BaseModel:
|
28
|
+
selected_tools = {x['name']: x['args_schema'].schema() for x in ZephyrSquadApiWrapper.model_construct().get_available_tools()}
|
29
|
+
ZephyrSquadToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
|
30
|
+
return create_model(
|
31
|
+
"zephyr_squad",
|
32
|
+
account_id=(str, Field(description="AccountID for the user that is going to be authenticating")),
|
33
|
+
access_key=(str, Field(description="Generated access key")),
|
34
|
+
secret_key=(SecretStr, Field(description="Generated secret key")),
|
35
|
+
selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
|
36
|
+
__config__={'json_schema_extra': {'metadata': {"label": "Zephyr Squad", "icon_url": "zephyr.svg",
|
37
|
+
"categories": ["test management"],
|
38
|
+
"extra_categories": ["test automation", "test case management", "test planning"]
|
39
|
+
}}}
|
40
|
+
)
|
41
|
+
|
42
|
+
@classmethod
|
43
|
+
def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
|
44
|
+
zephyr_api_wrapper = ZephyrSquadApiWrapper(**kwargs)
|
45
|
+
prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
|
46
|
+
available_tools = zephyr_api_wrapper.get_available_tools()
|
47
|
+
tools = []
|
48
|
+
for tool in available_tools:
|
49
|
+
if selected_tools:
|
50
|
+
if tool["name"] not in selected_tools:
|
51
|
+
continue
|
52
|
+
tools.append(BaseAction(
|
53
|
+
api_wrapper=zephyr_api_wrapper,
|
54
|
+
name=prefix + tool["name"],
|
55
|
+
description=tool["description"],
|
56
|
+
args_schema=tool["args_schema"]
|
57
|
+
))
|
58
|
+
return cls(tools=tools)
|
59
|
+
|
60
|
+
def get_tools(self):
|
61
|
+
return self.tools
|
62
|
+
|
@@ -0,0 +1,135 @@
|
|
1
|
+
from typing import List, Literal
|
2
|
+
|
3
|
+
from pydantic import model_validator, create_model, Field, SecretStr, BaseModel, PrivateAttr
|
4
|
+
|
5
|
+
from .zephyr_squad_cloud_client import ZephyrSquadCloud
|
6
|
+
from ..elitea_base import BaseToolApiWrapper
|
7
|
+
|
8
|
+
|
9
|
+
class ZephyrSquadApiWrapper(BaseToolApiWrapper):
|
10
|
+
account_id: str
|
11
|
+
access_key: str
|
12
|
+
secret_key: SecretStr
|
13
|
+
_client: ZephyrSquadCloud = PrivateAttr()
|
14
|
+
|
15
|
+
@model_validator(mode='before')
|
16
|
+
@classmethod
|
17
|
+
def validate_toolkit(cls, values):
|
18
|
+
account_id = values.get("account_id", None)
|
19
|
+
access_key = values.get("access_key", None)
|
20
|
+
secret_key = values.get("secret_key", None)
|
21
|
+
if not account_id:
|
22
|
+
raise ValueError("account_id is required.")
|
23
|
+
if not access_key:
|
24
|
+
raise ValueError("access_key is required.")
|
25
|
+
if not secret_key:
|
26
|
+
raise ValueError("secret_key is required.")
|
27
|
+
cls._client = ZephyrSquadCloud(
|
28
|
+
account_id=account_id,
|
29
|
+
access_key=access_key,
|
30
|
+
secret_key=secret_key
|
31
|
+
)
|
32
|
+
return values
|
33
|
+
|
34
|
+
def get_test_step(self, issue_id, step_id, project_id):
|
35
|
+
"""Retrieve details for a specific test step in a Jira test case."""
|
36
|
+
return self._client.get_test_step(issue_id, step_id, project_id)
|
37
|
+
|
38
|
+
def update_test_step(self, issue_id, step_id, project_id, json):
|
39
|
+
"""Update the content or a specific test step in a Jira test case."""
|
40
|
+
return self._client.update_test_step(issue_id, step_id, project_id, json)
|
41
|
+
|
42
|
+
def delete_test_step(self, issue_id, step_id, project_id):
|
43
|
+
"""Remove a specific test step from a Jira test case."""
|
44
|
+
return self._client.delete_test_step(issue_id, step_id, project_id)
|
45
|
+
|
46
|
+
def create_new_test_step(self, issue_id, project_id, json):
|
47
|
+
"""Add a new test step to a Jira test case."""
|
48
|
+
return self._client.create_new_test_step(issue_id, project_id, json)
|
49
|
+
|
50
|
+
def get_all_test_steps(self, issue_id, project_id):
|
51
|
+
"""List all test steps associated with a Jira test case."""
|
52
|
+
return self._client.get_all_test_steps(issue_id, project_id)
|
53
|
+
|
54
|
+
def get_all_test_step_statuses(self):
|
55
|
+
"""Retrieve all possible statuses for test steps in Jira."""
|
56
|
+
return self._client.get_all_test_step_statuses()
|
57
|
+
|
58
|
+
def get_available_tools(self):
|
59
|
+
return [
|
60
|
+
{
|
61
|
+
"name": "get_test_step",
|
62
|
+
"description": self.get_test_step.__doc__,
|
63
|
+
"args_schema": ProjectIssueStep,
|
64
|
+
"ref": self.get_test_step,
|
65
|
+
},
|
66
|
+
{
|
67
|
+
"name": "update_test_step",
|
68
|
+
"description": self.update_test_step.__doc__,
|
69
|
+
"args_schema": UpdateTestStep,
|
70
|
+
"ref": self.update_test_step,
|
71
|
+
},
|
72
|
+
{
|
73
|
+
"name": "delete_test_step",
|
74
|
+
"description": self.delete_test_step.__doc__,
|
75
|
+
"args_schema": ProjectIssueStep,
|
76
|
+
"ref": self.delete_test_step,
|
77
|
+
},
|
78
|
+
{
|
79
|
+
"name": "create_new_test_step",
|
80
|
+
"description": self.create_new_test_step.__doc__,
|
81
|
+
"args_schema": CreateNewTestStep,
|
82
|
+
"ref": self.create_new_test_step,
|
83
|
+
},
|
84
|
+
{
|
85
|
+
"name": "get_all_test_steps",
|
86
|
+
"description": self.get_all_test_steps.__doc__,
|
87
|
+
"args_schema": ProjectIssue,
|
88
|
+
"ref": self.get_all_test_steps,
|
89
|
+
},
|
90
|
+
{
|
91
|
+
"name": "get_all_test_step_statuses",
|
92
|
+
"description": self.get_all_test_step_statuses.__doc__,
|
93
|
+
"args_schema": create_model("NoInput"),
|
94
|
+
"ref": self.get_all_test_step_statuses,
|
95
|
+
}
|
96
|
+
]
|
97
|
+
|
98
|
+
|
99
|
+
ProjectIssue = create_model(
|
100
|
+
"ProjectIssue",
|
101
|
+
issue_id=(int, Field(description="Jira ticket id of test case to which the test step belongs.")),
|
102
|
+
project_id=(int, Field(description="Jira project id to which test case belongs."))
|
103
|
+
)
|
104
|
+
|
105
|
+
ProjectIssueStep = create_model(
|
106
|
+
"ProjectIssueStep",
|
107
|
+
step_id=(str, Field(description="Test step id to operate.")),
|
108
|
+
__base__=ProjectIssue
|
109
|
+
)
|
110
|
+
|
111
|
+
UpdateTestStep = create_model(
|
112
|
+
"UpdateTestStep",
|
113
|
+
json=(str, Field(description=(
|
114
|
+
"JSON body to update a Zephyr test step. Fields:\n"
|
115
|
+
"- id (string, required): Unique identifier for the test step. Example: \"0001481146115453-3a0480a3ffffc384-0001\"\n"
|
116
|
+
"- step (string, required): Description of the test step. Example: \"Sample Test Step\"\n"
|
117
|
+
"- data (string, optional): Test data used in this step. Example: \"Sample Test Data\"\n"
|
118
|
+
"- result (string, optional): Expected result after executing the step. Example: \"Expected Test Result\"\n"
|
119
|
+
"- customFieldValues (array[object], optional): List of custom field values for the test step. Each object contains:\n"
|
120
|
+
" - customFieldId (string, required): ID of the custom field. Example: \"3ce1c679-7c43-4d37-89f6-757603379e31\"\n"
|
121
|
+
" - value (object, required): Value for the custom field. Example: {\"value\": \"08/21/2018\"}\n"
|
122
|
+
"*IMPORTANT*: Use double quotes for all field names and string values."))),
|
123
|
+
__base__=ProjectIssueStep
|
124
|
+
)
|
125
|
+
|
126
|
+
CreateNewTestStep = create_model(
|
127
|
+
"CreateNewTestStep",
|
128
|
+
json=(str, Field(description=(
|
129
|
+
"JSON body to create a Zephyr test step. Fields:\n"
|
130
|
+
"- step (string, required): Description of the test step. Example: \"Sample Test Step\"\n"
|
131
|
+
"- data (string, optional): Test data used in this step. Example: \"Sample Test Data\"\n"
|
132
|
+
"- result (string, optional): Expected result after executing the step. Example: \"Expected Test Result\"\n"
|
133
|
+
"*IMPORTANT*: Use double quotes for all field names and string values."))),
|
134
|
+
__base__=ProjectIssue
|
135
|
+
)
|
@@ -0,0 +1,79 @@
|
|
1
|
+
import hashlib
|
2
|
+
import time
|
3
|
+
|
4
|
+
import jwt
|
5
|
+
import requests
|
6
|
+
from langchain_core.tools import ToolException
|
7
|
+
|
8
|
+
|
9
|
+
class ZephyrSquadCloud(object):
|
10
|
+
"""
|
11
|
+
Reference: https://zephyrsquad.docs.apiary.io//
|
12
|
+
"""
|
13
|
+
|
14
|
+
def __init__(self, account_id, access_key, secret_key):
|
15
|
+
self.account_id = account_id
|
16
|
+
self.access_key = access_key
|
17
|
+
self.secret_key = secret_key
|
18
|
+
self.base_url = "https://prod-api.zephyr4jiracloud.com/connect"
|
19
|
+
|
20
|
+
def get_test_step(self, issue_id, step_id, project_id):
|
21
|
+
canonical_path = "/public/rest/api/1.0/teststep/issueId/id?projectId="
|
22
|
+
api_path = f"/public/rest/api/1.0/teststep/{issue_id}/{step_id}?projectId={project_id}"
|
23
|
+
return self._do_request(method="GET", api_path=api_path, canonical_path=canonical_path)
|
24
|
+
|
25
|
+
def update_test_step(self, issue_id, step_id, project_id, json):
|
26
|
+
canonical_path = "/public/rest/api/1.0/teststep/issueId/id?projectId="
|
27
|
+
api_path = f"/public/rest/api/1.0/teststep/{issue_id}/{step_id}?projectId={project_id}"
|
28
|
+
return self._do_request(method="PUT", api_path=api_path, canonical_path=canonical_path, json=json)
|
29
|
+
|
30
|
+
def delete_test_step(self, issue_id, step_id, project_id):
|
31
|
+
canonical_path = "/public/rest/api/1.0/teststep/issueId/id?projectId="
|
32
|
+
api_path = f"/public/rest/api/1.0/teststep/{issue_id}/{step_id}?projectId={project_id}"
|
33
|
+
return self._do_request(method="DELETE", api_path=api_path, canonical_path=canonical_path)
|
34
|
+
|
35
|
+
def create_new_test_step(self, issue_id, project_id, json):
|
36
|
+
canonical_path = "/public/rest/api/1.0/teststep/issueId?projectId="
|
37
|
+
api_path = f"/public/rest/api/1.0/teststep/{issue_id}?projectId={project_id}"
|
38
|
+
return self._do_request(method="POST", api_path=api_path, canonical_path=canonical_path, json=json)
|
39
|
+
|
40
|
+
def get_all_test_steps(self, issue_id, project_id):
|
41
|
+
canonical_path = "/public/rest/api/2.0/teststep/issueId?projectId="
|
42
|
+
api_path = f"/public/rest/api/2.0/teststep/{issue_id}?projectId={project_id}"
|
43
|
+
return self._do_request(method='GET', api_path=api_path, canonical_path=canonical_path)
|
44
|
+
|
45
|
+
def get_all_test_step_statuses(self):
|
46
|
+
api_path = "/public/rest/api/1.0/teststep/statuses"
|
47
|
+
return self._do_request(method='GET', api_path=api_path)
|
48
|
+
|
49
|
+
def _do_request(self, method, api_path, canonical_path=None, json=None):
|
50
|
+
url = f"{self.base_url}{api_path}"
|
51
|
+
headers = {
|
52
|
+
"Authorization": f"JWT {self._generate_jwt_token(method, canonical_path or api_path)}",
|
53
|
+
"zapiAccessKey": self.access_key,
|
54
|
+
"Content-Type": "application/json"
|
55
|
+
}
|
56
|
+
|
57
|
+
try:
|
58
|
+
resp = requests.request(method=method, url=url, json=json, headers=headers)
|
59
|
+
|
60
|
+
if resp.ok:
|
61
|
+
if resp.headers.get("Content-Type", "").startswith("application/json"):
|
62
|
+
return str(resp.json())
|
63
|
+
else:
|
64
|
+
return resp.text
|
65
|
+
else:
|
66
|
+
raise ToolException(f"Request failed with status {resp.status_code}: {resp.text}")
|
67
|
+
except Exception as e:
|
68
|
+
raise ToolException(f"Error performing request {method}:{api_path}: {e}")
|
69
|
+
|
70
|
+
def _generate_jwt_token(self, method, path):
|
71
|
+
canonical_path = f"{method}&{path}&"
|
72
|
+
payload_token = {
|
73
|
+
'sub': self.account_id,
|
74
|
+
'qsh': hashlib.sha256(canonical_path.encode('utf-8')).hexdigest(),
|
75
|
+
'iss': self.access_key,
|
76
|
+
'exp': int(time.time()) + 3600,
|
77
|
+
'iat': int(time.time())
|
78
|
+
}
|
79
|
+
return jwt.encode(payload_token, self.secret_key, algorithm='HS256').strip()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: alita_sdk
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.163
|
4
4
|
Summary: SDK for building langchain agents using resources from Alita
|
5
5
|
Author-email: Artem Rozumenko <artyom.rozumenko@gmail.com>, Mikalai Biazruchka <mikalai_biazruchka@epam.com>, Roman Mitusov <roman_mitusov@epam.com>, Ivan Krakhmaliuk <lifedjik@gmail.com>
|
6
6
|
License-Expression: Apache-2.0
|
@@ -129,7 +129,7 @@ alita_sdk/runtime/utils/logging.py,sha256=svPyiW8ztDfhqHFITv5FBCj8UhLxz6hWcqGIY6
|
|
129
129
|
alita_sdk/runtime/utils/save_dataframe.py,sha256=i-E1wp-t4wb17Zq3nA3xYwgSILjoXNizaQAA9opWvxY,1576
|
130
130
|
alita_sdk/runtime/utils/streamlit.py,sha256=z4J_bdxkA0zMROkvTB4u379YBRFCkKh-h7PD8RlnZWQ,85644
|
131
131
|
alita_sdk/runtime/utils/utils.py,sha256=dM8whOJAuFJFe19qJ69-FLzrUp6d2G-G6L7d4ss2XqM,346
|
132
|
-
alita_sdk/tools/__init__.py,sha256=
|
132
|
+
alita_sdk/tools/__init__.py,sha256=Ttjp6AVkjuh-WqGE_cc_nUwi4qvU1khf7_BVKdwO7gU,9801
|
133
133
|
alita_sdk/tools/elitea_base.py,sha256=NQaIxPX6DVIerHCb18jwUR6maZxxk73NZaTsFHkBQWE,21119
|
134
134
|
alita_sdk/tools/ado/__init__.py,sha256=mD6GHcYMTtffPJkJvFPe2rzvye_IRmXmWfI7xYuZhO4,912
|
135
135
|
alita_sdk/tools/ado/utils.py,sha256=PTCludvaQmPLakF2EbCGy66Mro4-rjDtavVP-xcB2Wc,1252
|
@@ -160,14 +160,20 @@ alita_sdk/tools/browser/google_search_rag.py,sha256=QVHFbVwymiJGuno_HLSJOK1c_Mpg
|
|
160
160
|
alita_sdk/tools/browser/utils.py,sha256=4k3YM_f1Kqlhjz9vt2pNsGkvCjhy-EmY3nvcwdFCsLA,2501
|
161
161
|
alita_sdk/tools/browser/wiki.py,sha256=Qh3HBFd4dkS2VavXbFJOm4b8SjVSIe5xSD7CY1vEkKE,1126
|
162
162
|
alita_sdk/tools/carrier/__init__.py,sha256=pP-nk-dpqOkrvwcRY_szgwqoowyVNl_GobD4Inp-Qus,4435
|
163
|
-
alita_sdk/tools/carrier/api_wrapper.py,sha256=
|
163
|
+
alita_sdk/tools/carrier/api_wrapper.py,sha256=smcc1Q7H6U1_18qDYBFpeGnu5cX3OrsUoKaSn3s6vkw,8728
|
164
164
|
alita_sdk/tools/carrier/backend_reports_tool.py,sha256=WNZVGBIZusakOdbd7lG6o6xL180VZfER-uDw_SSGupo,11005
|
165
165
|
alita_sdk/tools/carrier/backend_tests_tool.py,sha256=arq275qiP9t3ST-MPn7FlxbLLSPiIGEnyPdgJ-AvOoQ,5917
|
166
|
-
alita_sdk/tools/carrier/
|
166
|
+
alita_sdk/tools/carrier/cancel_ui_test_tool.py,sha256=pD1sKEcZGBWJqFpgjeohMk93uuUPWruVJRPVVg90rpo,6438
|
167
|
+
alita_sdk/tools/carrier/carrier_sdk.py,sha256=sYdPWcpH8ti0MggOvU2pbsKYiaKR1zuXlbiCtcTfc3A,12913
|
168
|
+
alita_sdk/tools/carrier/create_ui_excel_report_tool.py,sha256=sYAz54ILk8CIF_n76zH_hcmbW9xw7oTNnf_d9d-N-_Q,20171
|
169
|
+
alita_sdk/tools/carrier/create_ui_test_tool.py,sha256=sHi7-D1uqIUHEyoywI92h6MdUVybKfBXs_XttTu-Ck4,8624
|
167
170
|
alita_sdk/tools/carrier/excel_reporter.py,sha256=fXptz7iaBDBcFSc8Ah8nZ9CSgugTONc5JMC1XcQEnfM,21487
|
171
|
+
alita_sdk/tools/carrier/lighthouse_excel_reporter.py,sha256=mVuU63tl2n-Gntx9RuedjEU0U5AP1APKsSx1DvJs7wk,6684
|
172
|
+
alita_sdk/tools/carrier/run_ui_test_tool.py,sha256=vmEtP-cpnOCyLLZfbw9Nq2ItGUFlcnsf1LmI_XyLrM8,21053
|
168
173
|
alita_sdk/tools/carrier/tickets_tool.py,sha256=d-wFyFWWTvV01o-hyssb2S-oLnr51b6tlNTUqA_CohY,8099
|
169
|
-
alita_sdk/tools/carrier/tools.py,sha256=
|
170
|
-
alita_sdk/tools/carrier/ui_reports_tool.py,sha256=
|
174
|
+
alita_sdk/tools/carrier/tools.py,sha256=cCLYcNzC_z0-AaqB46fyt7iOeP20LStVQKI5Dg1zWA4,1588
|
175
|
+
alita_sdk/tools/carrier/ui_reports_tool.py,sha256=Y6EstTRCa9d11ipFUFGOYlpiEhFx7aOQcgZ_M5Gd1lQ,13708
|
176
|
+
alita_sdk/tools/carrier/update_ui_test_schedule_tool.py,sha256=jh9Q86cMCEqpsFopJPNIP0wlr7sYVa_3lhlq6lRmkGg,11850
|
171
177
|
alita_sdk/tools/carrier/utils.py,sha256=rl7aq-F6ed_PapDM15w8EtS0BkgsjpDrNdKYuDCMOaI,4376
|
172
178
|
alita_sdk/tools/chunkers/__init__.py,sha256=myaBVvPbUsz6PXtBDpA4EiPQgLvIv3q_WPh86kxlccI,774
|
173
179
|
alita_sdk/tools/chunkers/models.py,sha256=NNkLSljZboYDj6vbqeHmcjj9JrTHbkVWmoHGsL98q3k,3032
|
@@ -317,8 +323,11 @@ alita_sdk/tools/zephyr_enterprise/api_wrapper.py,sha256=Ir3zHljhbZQJRJJQOBzS_GL5
|
|
317
323
|
alita_sdk/tools/zephyr_enterprise/zephyr_enterprise.py,sha256=hV9LIrYfJT6oYp-ZfQR0YHflqBFPsUw2Oc55HwK0H48,6809
|
318
324
|
alita_sdk/tools/zephyr_scale/__init__.py,sha256=2NTcdrfkx4GSegqyXhsPLsEpc4FlACuDy85b0fk6cAo,4572
|
319
325
|
alita_sdk/tools/zephyr_scale/api_wrapper.py,sha256=UHVQUVqcBc3SZvDfO78HSuBzwAsRw2cCDQa-xMOzndE,68663
|
320
|
-
alita_sdk
|
321
|
-
alita_sdk
|
322
|
-
alita_sdk
|
323
|
-
alita_sdk-0.3.
|
324
|
-
alita_sdk-0.3.
|
326
|
+
alita_sdk/tools/zephyr_squad/__init__.py,sha256=rq4jOb3lRW2GXvAguk4H1KinO5f-zpygzhBJf-E1Ucw,2773
|
327
|
+
alita_sdk/tools/zephyr_squad/api_wrapper.py,sha256=iOMxyE7vOc_LwFB_nBMiSFXkNtvbptA4i-BrTlo7M0A,5854
|
328
|
+
alita_sdk/tools/zephyr_squad/zephyr_squad_cloud_client.py,sha256=IYUJoMFOMA70knLhLtAnuGoy3OK80RuqeQZ710oyIxE,3631
|
329
|
+
alita_sdk-0.3.163.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
330
|
+
alita_sdk-0.3.163.dist-info/METADATA,sha256=3Fd8T3ods52lKrlnTjGDxEAn_svB00x64xg65cCi8rY,18667
|
331
|
+
alita_sdk-0.3.163.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
332
|
+
alita_sdk-0.3.163.dist-info/top_level.txt,sha256=0vJYy5p_jK6AwVb1aqXr7Kgqgk3WDtQ6t5C-XI9zkmg,10
|
333
|
+
alita_sdk-0.3.163.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|