lucius-mcp 0.3.0__py3-none-any.whl → 0.4.0__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.
- lucius_mcp-0.4.0.dist-info/METADATA +124 -0
- {lucius_mcp-0.3.0.dist-info → lucius_mcp-0.4.0.dist-info}/RECORD +38 -29
- src/client/__init__.py +6 -0
- src/client/client.py +271 -7
- src/client/exceptions.py +23 -0
- src/client/generated/README.md +18 -0
- src/client/generated/__init__.py +2 -0
- src/client/generated/api/__init__.py +1 -0
- src/client/generated/api/integration_controller_api.py +5285 -0
- src/client/generated/docs/IntegrationControllerApi.md +1224 -0
- src/services/__init__.py +9 -1
- src/services/custom_field_value_service.py +301 -0
- src/services/integration_service.py +205 -0
- src/services/launch_service.py +29 -1
- src/services/shared_step_service.py +34 -17
- src/services/test_case_service.py +269 -37
- src/services/test_layer_service.py +11 -0
- src/tools/__init__.py +19 -1
- src/tools/create_custom_field_value.py +38 -0
- src/tools/create_test_case.py +32 -2
- src/tools/delete_custom_field_value.py +57 -0
- src/tools/delete_test_case.py +5 -4
- src/tools/delete_test_layer.py +17 -4
- src/tools/delete_test_layer_schema.py +17 -4
- src/tools/launches.py +86 -0
- src/tools/link_shared_step.py +18 -12
- src/tools/list_custom_field_values.py +72 -0
- src/tools/list_integrations.py +77 -0
- src/tools/search.py +3 -3
- src/tools/shared_steps.py +23 -8
- src/tools/unlink_shared_step.py +19 -5
- src/tools/update_custom_field_value.py +55 -0
- src/tools/update_test_case.py +67 -2
- src/tools/update_test_layer.py +15 -4
- src/tools/update_test_layer_schema.py +16 -5
- lucius_mcp-0.3.0.dist-info/METADATA +0 -297
- {lucius_mcp-0.3.0.dist-info → lucius_mcp-0.4.0.dist-info}/WHEEL +0 -0
- {lucius_mcp-0.3.0.dist-info → lucius_mcp-0.4.0.dist-info}/entry_points.txt +0 -0
- {lucius_mcp-0.3.0.dist-info → lucius_mcp-0.4.0.dist-info}/licenses/LICENSE +0 -0
src/client/client.py
CHANGED
|
@@ -10,7 +10,7 @@ from collections.abc import Awaitable
|
|
|
10
10
|
from typing import Literal, TypeVar, cast, overload
|
|
11
11
|
|
|
12
12
|
import httpx
|
|
13
|
-
from pydantic import SecretStr, ValidationError
|
|
13
|
+
from pydantic import Field, SecretStr, ValidationError
|
|
14
14
|
|
|
15
15
|
from src.client.exceptions import TestCaseNotFoundError
|
|
16
16
|
from src.utils.config import settings
|
|
@@ -26,7 +26,9 @@ from .exceptions import (
|
|
|
26
26
|
from .generated.api.custom_field_controller_api import CustomFieldControllerApi
|
|
27
27
|
from .generated.api.custom_field_project_controller_api import CustomFieldProjectControllerApi
|
|
28
28
|
from .generated.api.custom_field_project_controller_v2_api import CustomFieldProjectControllerV2Api
|
|
29
|
+
from .generated.api.custom_field_value_controller_api import CustomFieldValueControllerApi
|
|
29
30
|
from .generated.api.custom_field_value_project_controller_api import CustomFieldValueProjectControllerApi
|
|
31
|
+
from .generated.api.integration_controller_api import IntegrationControllerApi
|
|
30
32
|
from .generated.api.launch_controller_api import LaunchControllerApi
|
|
31
33
|
from .generated.api.launch_search_controller_api import LaunchSearchControllerApi
|
|
32
34
|
from .generated.api.shared_step_attachment_controller_api import SharedStepAttachmentControllerApi
|
|
@@ -46,17 +48,23 @@ from .generated.models.aql_validate_response_dto import AqlValidateResponseDto
|
|
|
46
48
|
from .generated.models.attachment_step_dto import AttachmentStepDto
|
|
47
49
|
from .generated.models.body_step_dto import BodyStepDto
|
|
48
50
|
from .generated.models.custom_field_project_with_values_dto import CustomFieldProjectWithValuesDto
|
|
51
|
+
from .generated.models.custom_field_value_project_create_dto import CustomFieldValueProjectCreateDto
|
|
52
|
+
from .generated.models.custom_field_value_project_patch_dto import CustomFieldValueProjectPatchDto
|
|
49
53
|
from .generated.models.custom_field_value_with_cf_dto import CustomFieldValueWithCfDto
|
|
50
54
|
from .generated.models.custom_field_with_values_dto import CustomFieldWithValuesDto
|
|
51
55
|
from .generated.models.find_all29200_response import FindAll29200Response
|
|
56
|
+
from .generated.models.integration_dto import IntegrationDto
|
|
57
|
+
from .generated.models.issue_dto import IssueDto
|
|
52
58
|
from .generated.models.launch_create_dto import LaunchCreateDto
|
|
53
59
|
from .generated.models.launch_dto import LaunchDto
|
|
60
|
+
from .generated.models.page_custom_field_value_with_tc_count_dto import PageCustomFieldValueWithTcCountDto
|
|
54
61
|
from .generated.models.page_launch_dto import PageLaunchDto
|
|
55
62
|
from .generated.models.page_launch_preview_dto import PageLaunchPreviewDto
|
|
56
63
|
from .generated.models.page_shared_step_dto import PageSharedStepDto
|
|
57
64
|
from .generated.models.page_test_case_dto import PageTestCaseDto
|
|
58
65
|
from .generated.models.scenario_step_create_dto import ScenarioStepCreateDto
|
|
59
66
|
from .generated.models.scenario_step_created_response_dto import ScenarioStepCreatedResponseDto
|
|
67
|
+
from .generated.models.scenario_step_patch_dto import ScenarioStepPatchDto
|
|
60
68
|
from .generated.models.shared_step_attachment_row_dto import SharedStepAttachmentRowDto
|
|
61
69
|
from .generated.models.shared_step_create_dto import SharedStepCreateDto
|
|
62
70
|
from .generated.models.shared_step_dto import SharedStepDto
|
|
@@ -77,9 +85,13 @@ from .overridden.test_case_custom_fields_v2 import TestCaseCustomFieldV2Controll
|
|
|
77
85
|
|
|
78
86
|
# Subclasses to add missing fields to generated models
|
|
79
87
|
class TestCaseDtoWithCF(TestCaseDto):
|
|
80
|
-
"""Subclass to support custom_fields access."""
|
|
88
|
+
"""Subclass to support custom_fields and issues access."""
|
|
81
89
|
|
|
82
90
|
custom_fields: list[CustomFieldValueWithCfDto] | None = None
|
|
91
|
+
issues: list[IssueDto] | None = None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
TestCaseDtoWithCF.model_rebuild()
|
|
83
95
|
|
|
84
96
|
|
|
85
97
|
class BodyStepDtoWithSteps(BodyStepDto):
|
|
@@ -89,6 +101,14 @@ class BodyStepDtoWithSteps(BodyStepDto):
|
|
|
89
101
|
id: int | None = None
|
|
90
102
|
|
|
91
103
|
|
|
104
|
+
class StepWithExpected(BodyStepDto):
|
|
105
|
+
"""Subclass to support expected results and nested steps."""
|
|
106
|
+
|
|
107
|
+
expected_result: str | None = Field(default=None, alias="expectedResult")
|
|
108
|
+
steps: list[SharedStepScenarioDtoStepsInner] | None = None
|
|
109
|
+
id: int | None = None
|
|
110
|
+
|
|
111
|
+
|
|
92
112
|
class AttachmentStepDtoWithName(AttachmentStepDto):
|
|
93
113
|
"""Subclass to support name attribute and id."""
|
|
94
114
|
|
|
@@ -119,11 +139,13 @@ type ApiType = (
|
|
|
119
139
|
| CustomFieldControllerApi
|
|
120
140
|
| CustomFieldProjectControllerApi
|
|
121
141
|
| CustomFieldProjectControllerV2Api
|
|
142
|
+
| CustomFieldValueControllerApi
|
|
122
143
|
| CustomFieldValueProjectControllerApi
|
|
123
144
|
| TestLayerControllerApi
|
|
124
145
|
| TestLayerSchemaControllerApi
|
|
125
146
|
| LaunchControllerApi
|
|
126
147
|
| LaunchSearchControllerApi
|
|
148
|
+
| IntegrationControllerApi
|
|
127
149
|
)
|
|
128
150
|
|
|
129
151
|
type NormalizedScenarioDict = dict[str, object]
|
|
@@ -148,12 +170,14 @@ __all__ = [
|
|
|
148
170
|
"PageTestCaseDto",
|
|
149
171
|
"ScenarioStepCreateDto",
|
|
150
172
|
"ScenarioStepCreatedResponseDto",
|
|
173
|
+
"ScenarioStepPatchDto",
|
|
151
174
|
"SharedStepAttachmentRowDto",
|
|
152
175
|
"SharedStepCreateDto",
|
|
153
176
|
"SharedStepDto",
|
|
154
177
|
"SharedStepPatchDto",
|
|
155
178
|
"SharedStepScenarioDtoStepsInner",
|
|
156
179
|
"SharedStepStepDtoWithId",
|
|
180
|
+
"StepWithExpected",
|
|
157
181
|
"TestCaseAttachmentRowDto",
|
|
158
182
|
"TestCaseCreateV2Dto",
|
|
159
183
|
"TestCaseDto",
|
|
@@ -229,11 +253,13 @@ class AllureClient:
|
|
|
229
253
|
self._custom_field_api: CustomFieldControllerApi | None = None
|
|
230
254
|
self._custom_field_project_api: CustomFieldProjectControllerApi | None = None
|
|
231
255
|
self._custom_field_project_v2_api: CustomFieldProjectControllerV2Api | None = None
|
|
256
|
+
self._custom_field_value_api: CustomFieldValueControllerApi | None = None
|
|
232
257
|
self._custom_field_value_project_api: CustomFieldValueProjectControllerApi | None = None
|
|
233
258
|
self._test_layer_api: TestLayerControllerApi | None = None
|
|
234
259
|
self._test_layer_schema_api: TestLayerSchemaControllerApi | None = None
|
|
235
260
|
self._launch_api: LaunchControllerApi | None = None
|
|
236
261
|
self._launch_search_api: LaunchSearchControllerApi | None = None
|
|
262
|
+
self._integration_api: IntegrationControllerApi | None = None
|
|
237
263
|
self._is_entered = False
|
|
238
264
|
|
|
239
265
|
@classmethod
|
|
@@ -374,11 +400,13 @@ class AllureClient:
|
|
|
374
400
|
self._custom_field_api = CustomFieldControllerApi(self._api_client)
|
|
375
401
|
self._custom_field_project_api = CustomFieldProjectControllerApi(self._api_client)
|
|
376
402
|
self._custom_field_project_v2_api = CustomFieldProjectControllerV2Api(self._api_client)
|
|
403
|
+
self._custom_field_value_api = CustomFieldValueControllerApi(self._api_client)
|
|
377
404
|
self._custom_field_value_project_api = CustomFieldValueProjectControllerApi(self._api_client)
|
|
378
405
|
self._test_layer_api = TestLayerControllerApi(self._api_client)
|
|
379
406
|
self._test_layer_schema_api = TestLayerSchemaControllerApi(self._api_client)
|
|
380
407
|
self._launch_api = LaunchControllerApi(self._api_client)
|
|
381
408
|
self._launch_search_api = LaunchSearchControllerApi(self._api_client)
|
|
409
|
+
self._integration_api = IntegrationControllerApi(self._api_client)
|
|
382
410
|
|
|
383
411
|
@property
|
|
384
412
|
def api_client(self) -> ApiClient:
|
|
@@ -391,6 +419,21 @@ class AllureClient:
|
|
|
391
419
|
raise RuntimeError("AllureClient must be used as an async context manager")
|
|
392
420
|
return self._api_client
|
|
393
421
|
|
|
422
|
+
async def get_integrations(self) -> list[IntegrationDto]:
|
|
423
|
+
"""Fetch all integrations."""
|
|
424
|
+
# Ensure we have a valid client
|
|
425
|
+
if self._integration_api is None:
|
|
426
|
+
raise RuntimeError("AllureClient must be used as an async context manager")
|
|
427
|
+
|
|
428
|
+
try:
|
|
429
|
+
# Fetch first page with reasonable size
|
|
430
|
+
page = await self._integration_api.get_integrations(page=0, size=100)
|
|
431
|
+
return page.content or []
|
|
432
|
+
except Exception:
|
|
433
|
+
# Log warning or re-raise depending on strictness.
|
|
434
|
+
# For now return empty list to act as fallback.
|
|
435
|
+
return []
|
|
436
|
+
|
|
394
437
|
async def __aenter__(self) -> AllureClient:
|
|
395
438
|
"""Initialize the client session within an async context.
|
|
396
439
|
|
|
@@ -512,6 +555,11 @@ class AllureClient:
|
|
|
512
555
|
self, attr_name: Literal["_custom_field_project_v2_api"], *, error_name: str | None = None
|
|
513
556
|
) -> CustomFieldProjectControllerV2Api: ...
|
|
514
557
|
|
|
558
|
+
@overload
|
|
559
|
+
async def _get_api(
|
|
560
|
+
self, attr_name: Literal["_custom_field_value_api"], *, error_name: str | None = None
|
|
561
|
+
) -> CustomFieldValueControllerApi: ...
|
|
562
|
+
|
|
515
563
|
@overload
|
|
516
564
|
async def _get_api(
|
|
517
565
|
self, attr_name: Literal["_custom_field_value_project_api"], *, error_name: str | None = None
|
|
@@ -831,6 +879,28 @@ class AllureClient:
|
|
|
831
879
|
raise AllureValidationError("Unexpected launch list response from API") from e
|
|
832
880
|
return FindAll29200Response(preview_data)
|
|
833
881
|
|
|
882
|
+
async def get_launch(self, launch_id: int) -> LaunchDto:
|
|
883
|
+
"""Retrieve a specific launch by its ID.
|
|
884
|
+
|
|
885
|
+
Args:
|
|
886
|
+
launch_id: The unique ID of the launch.
|
|
887
|
+
|
|
888
|
+
Returns:
|
|
889
|
+
The launch data.
|
|
890
|
+
|
|
891
|
+
Raises:
|
|
892
|
+
AllureNotFoundError: If launch doesn't exist.
|
|
893
|
+
AllureValidationError: If input is invalid.
|
|
894
|
+
AllureAuthError: If unauthorized.
|
|
895
|
+
AllureAPIError: If the server returns an error.
|
|
896
|
+
"""
|
|
897
|
+
api = await self._get_api("_launch_api", error_name="launch APIs")
|
|
898
|
+
|
|
899
|
+
if not isinstance(launch_id, int) or launch_id <= 0:
|
|
900
|
+
raise AllureValidationError("Launch ID must be a positive integer")
|
|
901
|
+
|
|
902
|
+
return await self._call_api(api.find_one23(id=launch_id, _request_timeout=self._timeout))
|
|
903
|
+
|
|
834
904
|
async def search_launches_aql(
|
|
835
905
|
self,
|
|
836
906
|
project_id: int,
|
|
@@ -1088,6 +1158,165 @@ class AllureClient:
|
|
|
1088
1158
|
with_expected_result=with_expected_result,
|
|
1089
1159
|
)
|
|
1090
1160
|
|
|
1161
|
+
async def list_custom_field_values(
|
|
1162
|
+
self,
|
|
1163
|
+
project_id: int,
|
|
1164
|
+
custom_field_id: int,
|
|
1165
|
+
*,
|
|
1166
|
+
query: str | None = None,
|
|
1167
|
+
var_global: bool | None = None,
|
|
1168
|
+
test_case_search: str | None = None,
|
|
1169
|
+
page: int | None = None,
|
|
1170
|
+
size: int | None = None,
|
|
1171
|
+
sort: list[str] | None = None,
|
|
1172
|
+
) -> PageCustomFieldValueWithTcCountDto:
|
|
1173
|
+
"""List custom field values for a project field.
|
|
1174
|
+
|
|
1175
|
+
Args:
|
|
1176
|
+
project_id: Target project ID.
|
|
1177
|
+
custom_field_id: Target custom field ID (project-scoped).
|
|
1178
|
+
query: Optional search query.
|
|
1179
|
+
var_global: Optional global flag filter.
|
|
1180
|
+
test_case_search: Optional test case search filter.
|
|
1181
|
+
page: Zero-based page index.
|
|
1182
|
+
size: Page size.
|
|
1183
|
+
sort: Optional sort criteria.
|
|
1184
|
+
|
|
1185
|
+
Returns:
|
|
1186
|
+
Paginated custom field values with test case counts.
|
|
1187
|
+
|
|
1188
|
+
Raises:
|
|
1189
|
+
AllureNotFoundError: If project or custom field doesn't exist.
|
|
1190
|
+
AllureValidationError: If input data fails validation.
|
|
1191
|
+
AllureAuthError: If unauthorized.
|
|
1192
|
+
AllureAPIError: If the server returns an error.
|
|
1193
|
+
"""
|
|
1194
|
+
api = await self._get_api("_custom_field_value_project_api")
|
|
1195
|
+
|
|
1196
|
+
if not isinstance(project_id, int) or project_id <= 0:
|
|
1197
|
+
raise AllureValidationError("Project ID must be a positive integer")
|
|
1198
|
+
if not isinstance(custom_field_id, int) or custom_field_id == 0:
|
|
1199
|
+
raise AllureValidationError("Custom Field ID must be a non-zero integer")
|
|
1200
|
+
if page is not None and (not isinstance(page, int) or page < 0):
|
|
1201
|
+
raise AllureValidationError("Page must be a non-negative integer")
|
|
1202
|
+
if size is not None and (not isinstance(size, int) or size <= 0 or size > 1000):
|
|
1203
|
+
raise AllureValidationError("Size must be between 1 and 1000")
|
|
1204
|
+
|
|
1205
|
+
return await self._call_api(
|
|
1206
|
+
api.find_all22(
|
|
1207
|
+
project_id=project_id,
|
|
1208
|
+
custom_field_id=custom_field_id,
|
|
1209
|
+
query=query,
|
|
1210
|
+
var_global=var_global,
|
|
1211
|
+
test_case_search=test_case_search,
|
|
1212
|
+
page=page,
|
|
1213
|
+
size=size,
|
|
1214
|
+
sort=sort,
|
|
1215
|
+
_request_timeout=self._timeout,
|
|
1216
|
+
)
|
|
1217
|
+
)
|
|
1218
|
+
|
|
1219
|
+
async def create_custom_field_value(
|
|
1220
|
+
self, project_id: int, data: CustomFieldValueProjectCreateDto
|
|
1221
|
+
) -> CustomFieldValueWithCfDto:
|
|
1222
|
+
"""Create a custom field value in a project.
|
|
1223
|
+
|
|
1224
|
+
Args:
|
|
1225
|
+
project_id: Target project ID.
|
|
1226
|
+
data: Custom field value payload.
|
|
1227
|
+
|
|
1228
|
+
Returns:
|
|
1229
|
+
The created custom field value DTO.
|
|
1230
|
+
|
|
1231
|
+
Raises:
|
|
1232
|
+
AllureNotFoundError: If project doesn't exist.
|
|
1233
|
+
AllureValidationError: If input data fails validation.
|
|
1234
|
+
AllureAuthError: If unauthorized.
|
|
1235
|
+
AllureAPIError: If the server returns an error.
|
|
1236
|
+
"""
|
|
1237
|
+
api = await self._get_api("_custom_field_value_project_api")
|
|
1238
|
+
|
|
1239
|
+
if not isinstance(project_id, int) or project_id <= 0:
|
|
1240
|
+
raise AllureValidationError("Project ID must be a positive integer")
|
|
1241
|
+
|
|
1242
|
+
try:
|
|
1243
|
+
return await self._call_api(
|
|
1244
|
+
api.create26(
|
|
1245
|
+
project_id=project_id,
|
|
1246
|
+
custom_field_value_project_create_dto=data,
|
|
1247
|
+
_request_timeout=self._timeout,
|
|
1248
|
+
)
|
|
1249
|
+
)
|
|
1250
|
+
except AllureAPIError as exc:
|
|
1251
|
+
if exc.status_code == 409:
|
|
1252
|
+
raise AllureValidationError(
|
|
1253
|
+
"Duplicate custom field value name.",
|
|
1254
|
+
status_code=exc.status_code,
|
|
1255
|
+
response_body=exc.response_body,
|
|
1256
|
+
suggestions=["Use a unique custom field value name"],
|
|
1257
|
+
) from exc
|
|
1258
|
+
raise
|
|
1259
|
+
|
|
1260
|
+
async def update_custom_field_value(
|
|
1261
|
+
self, project_id: int, cfv_id: int, data: CustomFieldValueProjectPatchDto
|
|
1262
|
+
) -> None:
|
|
1263
|
+
"""Update a custom field value in a project.
|
|
1264
|
+
|
|
1265
|
+
Args:
|
|
1266
|
+
project_id: Target project ID.
|
|
1267
|
+
cfv_id: Target custom field value ID.
|
|
1268
|
+
data: Patch payload for the custom field value.
|
|
1269
|
+
|
|
1270
|
+
Raises:
|
|
1271
|
+
AllureNotFoundError: If project or value doesn't exist.
|
|
1272
|
+
AllureValidationError: If input data fails validation.
|
|
1273
|
+
AllureAuthError: If unauthorized.
|
|
1274
|
+
AllureAPIError: If the server returns an error.
|
|
1275
|
+
"""
|
|
1276
|
+
api = await self._get_api("_custom_field_value_project_api")
|
|
1277
|
+
|
|
1278
|
+
if not isinstance(project_id, int) or project_id <= 0:
|
|
1279
|
+
raise AllureValidationError("Project ID must be a positive integer")
|
|
1280
|
+
if not isinstance(cfv_id, int) or cfv_id <= 0:
|
|
1281
|
+
raise AllureValidationError("Custom Field Value ID must be a positive integer")
|
|
1282
|
+
|
|
1283
|
+
await self._call_api(
|
|
1284
|
+
api.patch23(
|
|
1285
|
+
project_id=project_id,
|
|
1286
|
+
cfv_id=cfv_id,
|
|
1287
|
+
custom_field_value_project_patch_dto=data,
|
|
1288
|
+
_request_timeout=self._timeout,
|
|
1289
|
+
)
|
|
1290
|
+
)
|
|
1291
|
+
|
|
1292
|
+
async def delete_custom_field_value(self, project_id: int, cfv_id: int) -> None:
|
|
1293
|
+
"""Delete a custom field value in a project.
|
|
1294
|
+
|
|
1295
|
+
Args:
|
|
1296
|
+
project_id: Target project ID.
|
|
1297
|
+
cfv_id: Target custom field value ID.
|
|
1298
|
+
|
|
1299
|
+
Raises:
|
|
1300
|
+
AllureNotFoundError: If project or value doesn't exist.
|
|
1301
|
+
AllureValidationError: If input data fails validation.
|
|
1302
|
+
AllureAuthError: If unauthorized.
|
|
1303
|
+
AllureAPIError: If the server returns an error.
|
|
1304
|
+
"""
|
|
1305
|
+
api = await self._get_api("_custom_field_value_project_api")
|
|
1306
|
+
|
|
1307
|
+
if not isinstance(project_id, int) or project_id <= 0:
|
|
1308
|
+
raise AllureValidationError("Project ID must be a positive integer")
|
|
1309
|
+
if not isinstance(cfv_id, int) or cfv_id <= 0:
|
|
1310
|
+
raise AllureValidationError("Custom Field Value ID must be a positive integer")
|
|
1311
|
+
|
|
1312
|
+
await self._call_api(
|
|
1313
|
+
api.delete47(
|
|
1314
|
+
project_id=project_id,
|
|
1315
|
+
id=cfv_id,
|
|
1316
|
+
_request_timeout=self._timeout,
|
|
1317
|
+
)
|
|
1318
|
+
)
|
|
1319
|
+
|
|
1091
1320
|
async def get_custom_fields_with_values(self, project_id: int) -> list[CustomFieldProjectWithValuesDto]:
|
|
1092
1321
|
"""Fetch all custom fields and their allowed values for a project.
|
|
1093
1322
|
|
|
@@ -1197,14 +1426,14 @@ class AllureClient:
|
|
|
1197
1426
|
AllureAPIError: If the API request fails.
|
|
1198
1427
|
"""
|
|
1199
1428
|
scenario_api = await self._get_api("_scenario_api")
|
|
1200
|
-
await self.
|
|
1201
|
-
scenario_api.
|
|
1429
|
+
await self._call_api_raw(
|
|
1430
|
+
scenario_api.delete_by_id1_without_preload_content(
|
|
1202
1431
|
id=step_id,
|
|
1203
1432
|
_request_timeout=self._timeout,
|
|
1204
1433
|
)
|
|
1205
1434
|
)
|
|
1206
1435
|
|
|
1207
|
-
async def get_test_case(self, test_case_id: int) ->
|
|
1436
|
+
async def get_test_case(self, test_case_id: int) -> TestCaseDtoWithCF:
|
|
1208
1437
|
"""Retrieve a specific test case by its ID.
|
|
1209
1438
|
|
|
1210
1439
|
Args:
|
|
@@ -1237,6 +1466,8 @@ class AllureClient:
|
|
|
1237
1466
|
)
|
|
1238
1467
|
if overview.custom_fields:
|
|
1239
1468
|
case.custom_fields = overview.custom_fields
|
|
1469
|
+
if overview.issues:
|
|
1470
|
+
case.issues = overview.issues
|
|
1240
1471
|
except Exception as e:
|
|
1241
1472
|
logger.warning(f"Failed to fetch overview for test case {test_case_id}: {e}")
|
|
1242
1473
|
|
|
@@ -1377,13 +1608,14 @@ class AllureClient:
|
|
|
1377
1608
|
child_ids = step_def.get("children") or []
|
|
1378
1609
|
child_steps = build_steps(child_ids) if child_ids else None
|
|
1379
1610
|
|
|
1380
|
-
# Build
|
|
1611
|
+
# Build StepWithExpected
|
|
1381
1612
|
steps_list.append(
|
|
1382
1613
|
SharedStepScenarioDtoStepsInner(
|
|
1383
|
-
actual_instance=
|
|
1614
|
+
actual_instance=StepWithExpected.model_construct(
|
|
1384
1615
|
type="BodyStepDto",
|
|
1385
1616
|
body=body,
|
|
1386
1617
|
body_json=None, # Skip complex rich-text
|
|
1618
|
+
expected_result=step_def.get("expectedResult"),
|
|
1387
1619
|
steps=child_steps,
|
|
1388
1620
|
id=sid,
|
|
1389
1621
|
)
|
|
@@ -1482,6 +1714,38 @@ class AllureClient:
|
|
|
1482
1714
|
|
|
1483
1715
|
return await self._create_scenario_step_via_api(shared_step_scenario_api, step)
|
|
1484
1716
|
|
|
1717
|
+
async def patch_test_case_scenario_step(
|
|
1718
|
+
self,
|
|
1719
|
+
step_id: int,
|
|
1720
|
+
patch: ScenarioStepPatchDto,
|
|
1721
|
+
) -> None:
|
|
1722
|
+
"""Patch a specific scenario step within a test case."""
|
|
1723
|
+
scenario_api = await self._get_api("_scenario_api")
|
|
1724
|
+
await self._call_api_raw(
|
|
1725
|
+
scenario_api.patch_by_id_without_preload_content(
|
|
1726
|
+
id=step_id,
|
|
1727
|
+
scenario_step_patch_dto=patch,
|
|
1728
|
+
with_expected_result=False,
|
|
1729
|
+
_request_timeout=self._timeout,
|
|
1730
|
+
)
|
|
1731
|
+
)
|
|
1732
|
+
|
|
1733
|
+
async def patch_shared_step_scenario_step(
|
|
1734
|
+
self,
|
|
1735
|
+
step_id: int,
|
|
1736
|
+
patch: ScenarioStepPatchDto,
|
|
1737
|
+
) -> None:
|
|
1738
|
+
"""Patch a specific scenario step within a shared step."""
|
|
1739
|
+
shared_step_scenario_api = await self._get_api("_shared_step_scenario_api")
|
|
1740
|
+
await self._call_api_raw(
|
|
1741
|
+
shared_step_scenario_api.patch_by_id1_without_preload_content(
|
|
1742
|
+
id=step_id,
|
|
1743
|
+
scenario_step_patch_dto=patch,
|
|
1744
|
+
with_expected_result=False,
|
|
1745
|
+
_request_timeout=self._timeout,
|
|
1746
|
+
)
|
|
1747
|
+
)
|
|
1748
|
+
|
|
1485
1749
|
async def upload_shared_step_attachment(
|
|
1486
1750
|
self,
|
|
1487
1751
|
shared_step_id: int,
|
src/client/exceptions.py
CHANGED
|
@@ -42,6 +42,28 @@ class TestCaseNotFoundError(ResourceNotFoundError):
|
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
|
|
45
|
+
class LaunchNotFoundError(ResourceNotFoundError):
|
|
46
|
+
"""Resource not found for a specific launch."""
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
launch_id: int,
|
|
51
|
+
status_code: int | None = None,
|
|
52
|
+
response_body: str | None = None,
|
|
53
|
+
) -> None:
|
|
54
|
+
suggestions = [
|
|
55
|
+
"Verify the launch ID",
|
|
56
|
+
"Use list_launches to find valid IDs",
|
|
57
|
+
"Check access to the project containing this launch",
|
|
58
|
+
]
|
|
59
|
+
super().__init__(
|
|
60
|
+
message=f"Launch ID {launch_id} not found",
|
|
61
|
+
status_code=status_code,
|
|
62
|
+
response_body=response_body,
|
|
63
|
+
suggestions=suggestions,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
45
67
|
class AllureAuthError(AuthenticationError):
|
|
46
68
|
"""Authentication failed (401/403)."""
|
|
47
69
|
|
|
@@ -57,5 +79,6 @@ __all__ = [
|
|
|
57
79
|
"AllureNotFoundError",
|
|
58
80
|
"AllureRateLimitError",
|
|
59
81
|
"AllureValidationError",
|
|
82
|
+
"LaunchNotFoundError",
|
|
60
83
|
"TestCaseNotFoundError",
|
|
61
84
|
]
|
src/client/generated/README.md
CHANGED
|
@@ -107,6 +107,24 @@ Class | Method | HTTP request | Description
|
|
|
107
107
|
*CustomFieldValueProjectControllerApi* | [**merge_custom_fields_to_existing_record**](src/client/generated/docs/CustomFieldValueProjectControllerApi.md#merge_custom_fields_to_existing_record) | **POST** /api/project/{projectId}/cfv/merge-to/{toCfvId} | Merge custom field values to existing record by id
|
|
108
108
|
*CustomFieldValueProjectControllerApi* | [**merge_custom_fields_to_new_record**](src/client/generated/docs/CustomFieldValueProjectControllerApi.md#merge_custom_fields_to_new_record) | **POST** /api/project/{projectId}/cfv/merge | Merge custom field values to new record
|
|
109
109
|
*CustomFieldValueProjectControllerApi* | [**patch23**](src/client/generated/docs/CustomFieldValueProjectControllerApi.md#patch23) | **PATCH** /api/project/{projectId}/cfv/{cfvId} | Patch specified custom field value, test results won't be affected
|
|
110
|
+
*IntegrationControllerApi* | [**create37**](src/client/generated/docs/IntegrationControllerApi.md#create37) | **POST** /api/integration |
|
|
111
|
+
*IntegrationControllerApi* | [**create_project_integration**](src/client/generated/docs/IntegrationControllerApi.md#create_project_integration) | **POST** /api/integration/project |
|
|
112
|
+
*IntegrationControllerApi* | [**delete_by_id3**](src/client/generated/docs/IntegrationControllerApi.md#delete_by_id3) | **DELETE** /api/integration/{id} |
|
|
113
|
+
*IntegrationControllerApi* | [**delete_project_integration**](src/client/generated/docs/IntegrationControllerApi.md#delete_project_integration) | **DELETE** /api/integration/{integrationId}/project/{projectId} |
|
|
114
|
+
*IntegrationControllerApi* | [**find_one_by_id**](src/client/generated/docs/IntegrationControllerApi.md#find_one_by_id) | **GET** /api/integration/{id} |
|
|
115
|
+
*IntegrationControllerApi* | [**find_project_integration_by_id**](src/client/generated/docs/IntegrationControllerApi.md#find_project_integration_by_id) | **GET** /api/integration/{integrationId}/project/{projectId} |
|
|
116
|
+
*IntegrationControllerApi* | [**get_available_integrations**](src/client/generated/docs/IntegrationControllerApi.md#get_available_integrations) | **GET** /api/integration/available |
|
|
117
|
+
*IntegrationControllerApi* | [**get_global_fields**](src/client/generated/docs/IntegrationControllerApi.md#get_global_fields) | **GET** /api/integration/globalfields |
|
|
118
|
+
*IntegrationControllerApi* | [**get_integration_projects**](src/client/generated/docs/IntegrationControllerApi.md#get_integration_projects) | **GET** /api/integration/{id}/project |
|
|
119
|
+
*IntegrationControllerApi* | [**get_integrations**](src/client/generated/docs/IntegrationControllerApi.md#get_integrations) | **GET** /api/integration |
|
|
120
|
+
*IntegrationControllerApi* | [**get_project_available_integrations**](src/client/generated/docs/IntegrationControllerApi.md#get_project_available_integrations) | **GET** /api/integration/project/{projectId}/available |
|
|
121
|
+
*IntegrationControllerApi* | [**get_project_integration_fields**](src/client/generated/docs/IntegrationControllerApi.md#get_project_integration_fields) | **GET** /api/integration/projectfields |
|
|
122
|
+
*IntegrationControllerApi* | [**get_project_integrations**](src/client/generated/docs/IntegrationControllerApi.md#get_project_integrations) | **GET** /api/integration/project/{projectId} |
|
|
123
|
+
*IntegrationControllerApi* | [**patch34**](src/client/generated/docs/IntegrationControllerApi.md#patch34) | **PATCH** /api/integration/{id} |
|
|
124
|
+
*IntegrationControllerApi* | [**patch_project_integration**](src/client/generated/docs/IntegrationControllerApi.md#patch_project_integration) | **PATCH** /api/integration/{integrationId}/project/{projectId} |
|
|
125
|
+
*IntegrationControllerApi* | [**suggest15**](src/client/generated/docs/IntegrationControllerApi.md#suggest15) | **GET** /api/integration/suggest | Suggest integrations
|
|
126
|
+
*IntegrationControllerApi* | [**validate1**](src/client/generated/docs/IntegrationControllerApi.md#validate1) | **POST** /api/integration/validate |
|
|
127
|
+
*IntegrationControllerApi* | [**validate2**](src/client/generated/docs/IntegrationControllerApi.md#validate2) | **POST** /api/integration/project/validate |
|
|
110
128
|
*LaunchControllerApi* | [**add_test_cases**](src/client/generated/docs/LaunchControllerApi.md#add_test_cases) | **POST** /api/launch/{id}/testcase/add | Add test cases to launch
|
|
111
129
|
*LaunchControllerApi* | [**add_test_plan**](src/client/generated/docs/LaunchControllerApi.md#add_test_plan) | **POST** /api/launch/{id}/testplan/add | Add test plan to launch
|
|
112
130
|
*LaunchControllerApi* | [**apply_defect_matchers**](src/client/generated/docs/LaunchControllerApi.md#apply_defect_matchers) | **POST** /api/launch/{id}/defect/apply | Apply defect matchers to launch
|
src/client/generated/__init__.py
CHANGED
|
@@ -24,6 +24,7 @@ __all__ = [
|
|
|
24
24
|
"CustomFieldSchemaControllerApi",
|
|
25
25
|
"CustomFieldValueControllerApi",
|
|
26
26
|
"CustomFieldValueProjectControllerApi",
|
|
27
|
+
"IntegrationControllerApi",
|
|
27
28
|
"LaunchControllerApi",
|
|
28
29
|
"LaunchSearchControllerApi",
|
|
29
30
|
"ProjectControllerApi",
|
|
@@ -742,6 +743,7 @@ from src.client.generated.api.custom_field_project_controller_v2_api import Cust
|
|
|
742
743
|
from src.client.generated.api.custom_field_schema_controller_api import CustomFieldSchemaControllerApi as CustomFieldSchemaControllerApi
|
|
743
744
|
from src.client.generated.api.custom_field_value_controller_api import CustomFieldValueControllerApi as CustomFieldValueControllerApi
|
|
744
745
|
from src.client.generated.api.custom_field_value_project_controller_api import CustomFieldValueProjectControllerApi as CustomFieldValueProjectControllerApi
|
|
746
|
+
from src.client.generated.api.integration_controller_api import IntegrationControllerApi as IntegrationControllerApi
|
|
745
747
|
from src.client.generated.api.launch_controller_api import LaunchControllerApi as LaunchControllerApi
|
|
746
748
|
from src.client.generated.api.launch_search_controller_api import LaunchSearchControllerApi as LaunchSearchControllerApi
|
|
747
749
|
from src.client.generated.api.project_controller_api import ProjectControllerApi as ProjectControllerApi
|
|
@@ -7,6 +7,7 @@ from src.client.generated.api.custom_field_project_controller_v2_api import Cust
|
|
|
7
7
|
from src.client.generated.api.custom_field_schema_controller_api import CustomFieldSchemaControllerApi
|
|
8
8
|
from src.client.generated.api.custom_field_value_controller_api import CustomFieldValueControllerApi
|
|
9
9
|
from src.client.generated.api.custom_field_value_project_controller_api import CustomFieldValueProjectControllerApi
|
|
10
|
+
from src.client.generated.api.integration_controller_api import IntegrationControllerApi
|
|
10
11
|
from src.client.generated.api.launch_controller_api import LaunchControllerApi
|
|
11
12
|
from src.client.generated.api.launch_search_controller_api import LaunchSearchControllerApi
|
|
12
13
|
from src.client.generated.api.project_controller_api import ProjectControllerApi
|