lucius-mcp 0.1.0__py3-none-any.whl → 0.2.2__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.1.0.dist-info → lucius_mcp-0.2.2.dist-info}/METADATA +23 -2
- {lucius_mcp-0.1.0.dist-info → lucius_mcp-0.2.2.dist-info}/RECORD +29 -14
- src/client/client.py +105 -0
- src/client/generated/README.md +65 -7
- src/client/generated/__init__.py +16 -0
- src/client/generated/api/__init__.py +8 -0
- src/client/generated/api/custom_field_controller_api.py +2617 -0
- src/client/generated/api/custom_field_project_controller_api.py +2337 -0
- src/client/generated/api/custom_field_project_controller_v2_api.py +659 -0
- src/client/generated/api/custom_field_schema_controller_api.py +1710 -0
- src/client/generated/api/custom_field_value_controller_api.py +3106 -0
- src/client/generated/api/custom_field_value_project_controller_api.py +1835 -0
- src/client/generated/api/project_controller_api.py +2986 -0
- src/client/generated/api/status_controller_api.py +1780 -0
- src/client/generated/docs/CustomFieldControllerApi.md +616 -0
- src/client/generated/docs/CustomFieldProjectControllerApi.md +554 -0
- src/client/generated/docs/CustomFieldProjectControllerV2Api.md +149 -0
- src/client/generated/docs/CustomFieldSchemaControllerApi.md +421 -0
- src/client/generated/docs/CustomFieldValueControllerApi.md +723 -0
- src/client/generated/docs/CustomFieldValueProjectControllerApi.md +430 -0
- src/client/generated/docs/LaunchControllerApi.md +2 -2
- src/client/generated/docs/ProjectControllerApi.md +717 -0
- src/client/generated/docs/StatusControllerApi.md +429 -0
- src/services/test_case_service.py +92 -33
- src/tools/__init__.py +3 -0
- src/tools/get_custom_fields.py +48 -0
- src/client/refactor-hotspots.md +0 -53
- src/client/refactor-plan.md +0 -78
- {lucius_mcp-0.1.0.dist-info → lucius_mcp-0.2.2.dist-info}/WHEEL +0 -0
- {lucius_mcp-0.1.0.dist-info → lucius_mcp-0.2.2.dist-info}/entry_points.txt +0 -0
- {lucius_mcp-0.1.0.dist-info → lucius_mcp-0.2.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# src.client.generated.StatusControllerApi
|
|
2
|
+
|
|
3
|
+
All URIs are relative to *http://localhost*
|
|
4
|
+
|
|
5
|
+
Method | HTTP request | Description
|
|
6
|
+
------------- | ------------- | -------------
|
|
7
|
+
[**create18**](StatusControllerApi.md#create18) | **POST** /api/status | Create a new status
|
|
8
|
+
[**delete17**](StatusControllerApi.md#delete17) | **DELETE** /api/status/{id} | Delete status by id
|
|
9
|
+
[**find_all15**](StatusControllerApi.md#find_all15) | **GET** /api/status | Find all statuses
|
|
10
|
+
[**find_one14**](StatusControllerApi.md#find_one14) | **GET** /api/status/{id} | Find status by id
|
|
11
|
+
[**patch17**](StatusControllerApi.md#patch17) | **PATCH** /api/status/{id} | Patch status
|
|
12
|
+
[**suggest8**](StatusControllerApi.md#suggest8) | **GET** /api/status/suggest | Suggest statuses
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# **create18**
|
|
16
|
+
> StatusDto create18(status_create_dto)
|
|
17
|
+
|
|
18
|
+
Create a new status
|
|
19
|
+
|
|
20
|
+
### Example
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
import src.client.generated
|
|
25
|
+
from src.client.generated.models.status_create_dto import StatusCreateDto
|
|
26
|
+
from src.client.generated.models.status_dto import StatusDto
|
|
27
|
+
from src.client.generated.rest import ApiException
|
|
28
|
+
from pprint import pprint
|
|
29
|
+
|
|
30
|
+
# Defining the host is optional and defaults to http://localhost
|
|
31
|
+
# See configuration.py for a list of all supported configuration parameters.
|
|
32
|
+
configuration = src.client.generated.Configuration(
|
|
33
|
+
host = "http://localhost"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Enter a context with an instance of the API client
|
|
38
|
+
async with src.client.generated.ApiClient(configuration) as api_client:
|
|
39
|
+
# Create an instance of the API class
|
|
40
|
+
api_instance = src.client.generated.StatusControllerApi(api_client)
|
|
41
|
+
status_create_dto = src.client.generated.StatusCreateDto() # StatusCreateDto |
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
# Create a new status
|
|
45
|
+
api_response = await api_instance.create18(status_create_dto)
|
|
46
|
+
print("The response of StatusControllerApi->create18:\n")
|
|
47
|
+
pprint(api_response)
|
|
48
|
+
except Exception as e:
|
|
49
|
+
print("Exception when calling StatusControllerApi->create18: %s\n" % e)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
### Parameters
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
Name | Type | Description | Notes
|
|
58
|
+
------------- | ------------- | ------------- | -------------
|
|
59
|
+
**status_create_dto** | [**StatusCreateDto**](StatusCreateDto.md)| |
|
|
60
|
+
|
|
61
|
+
### Return type
|
|
62
|
+
|
|
63
|
+
[**StatusDto**](StatusDto.md)
|
|
64
|
+
|
|
65
|
+
### Authorization
|
|
66
|
+
|
|
67
|
+
No authorization required
|
|
68
|
+
|
|
69
|
+
### HTTP request headers
|
|
70
|
+
|
|
71
|
+
- **Content-Type**: application/json
|
|
72
|
+
- **Accept**: */*
|
|
73
|
+
|
|
74
|
+
### HTTP response details
|
|
75
|
+
|
|
76
|
+
| Status code | Description | Response headers |
|
|
77
|
+
|-------------|-------------|------------------|
|
|
78
|
+
**200** | OK | - |
|
|
79
|
+
|
|
80
|
+
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
|
81
|
+
|
|
82
|
+
# **delete17**
|
|
83
|
+
> delete17(id)
|
|
84
|
+
|
|
85
|
+
Delete status by id
|
|
86
|
+
|
|
87
|
+
### Example
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
import src.client.generated
|
|
92
|
+
from src.client.generated.rest import ApiException
|
|
93
|
+
from pprint import pprint
|
|
94
|
+
|
|
95
|
+
# Defining the host is optional and defaults to http://localhost
|
|
96
|
+
# See configuration.py for a list of all supported configuration parameters.
|
|
97
|
+
configuration = src.client.generated.Configuration(
|
|
98
|
+
host = "http://localhost"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# Enter a context with an instance of the API client
|
|
103
|
+
async with src.client.generated.ApiClient(configuration) as api_client:
|
|
104
|
+
# Create an instance of the API class
|
|
105
|
+
api_instance = src.client.generated.StatusControllerApi(api_client)
|
|
106
|
+
id = 56 # int |
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
# Delete status by id
|
|
110
|
+
await api_instance.delete17(id)
|
|
111
|
+
except Exception as e:
|
|
112
|
+
print("Exception when calling StatusControllerApi->delete17: %s\n" % e)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
### Parameters
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
Name | Type | Description | Notes
|
|
121
|
+
------------- | ------------- | ------------- | -------------
|
|
122
|
+
**id** | **int**| |
|
|
123
|
+
|
|
124
|
+
### Return type
|
|
125
|
+
|
|
126
|
+
void (empty response body)
|
|
127
|
+
|
|
128
|
+
### Authorization
|
|
129
|
+
|
|
130
|
+
No authorization required
|
|
131
|
+
|
|
132
|
+
### HTTP request headers
|
|
133
|
+
|
|
134
|
+
- **Content-Type**: Not defined
|
|
135
|
+
- **Accept**: Not defined
|
|
136
|
+
|
|
137
|
+
### HTTP response details
|
|
138
|
+
|
|
139
|
+
| Status code | Description | Response headers |
|
|
140
|
+
|-------------|-------------|------------------|
|
|
141
|
+
**204** | No Content | - |
|
|
142
|
+
|
|
143
|
+
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
|
144
|
+
|
|
145
|
+
# **find_all15**
|
|
146
|
+
> PageStatusDto find_all15(workflow_id=workflow_id, page=page, size=size, sort=sort)
|
|
147
|
+
|
|
148
|
+
Find all statuses
|
|
149
|
+
|
|
150
|
+
### Example
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
import src.client.generated
|
|
155
|
+
from src.client.generated.models.page_status_dto import PageStatusDto
|
|
156
|
+
from src.client.generated.rest import ApiException
|
|
157
|
+
from pprint import pprint
|
|
158
|
+
|
|
159
|
+
# Defining the host is optional and defaults to http://localhost
|
|
160
|
+
# See configuration.py for a list of all supported configuration parameters.
|
|
161
|
+
configuration = src.client.generated.Configuration(
|
|
162
|
+
host = "http://localhost"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
# Enter a context with an instance of the API client
|
|
167
|
+
async with src.client.generated.ApiClient(configuration) as api_client:
|
|
168
|
+
# Create an instance of the API class
|
|
169
|
+
api_instance = src.client.generated.StatusControllerApi(api_client)
|
|
170
|
+
workflow_id = 56 # int | (optional)
|
|
171
|
+
page = 0 # int | Zero-based page index (0..N) (optional) (default to 0)
|
|
172
|
+
size = 10 # int | The size of the page to be returned (optional) (default to 10)
|
|
173
|
+
sort = [name,ASC] # List[str] | Sorting criteria in the format: property(,asc|desc). Default sort order is ascending. Multiple sort criteria are supported. (optional) (default to [name,ASC])
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
# Find all statuses
|
|
177
|
+
api_response = await api_instance.find_all15(workflow_id=workflow_id, page=page, size=size, sort=sort)
|
|
178
|
+
print("The response of StatusControllerApi->find_all15:\n")
|
|
179
|
+
pprint(api_response)
|
|
180
|
+
except Exception as e:
|
|
181
|
+
print("Exception when calling StatusControllerApi->find_all15: %s\n" % e)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
### Parameters
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
Name | Type | Description | Notes
|
|
190
|
+
------------- | ------------- | ------------- | -------------
|
|
191
|
+
**workflow_id** | **int**| | [optional]
|
|
192
|
+
**page** | **int**| Zero-based page index (0..N) | [optional] [default to 0]
|
|
193
|
+
**size** | **int**| The size of the page to be returned | [optional] [default to 10]
|
|
194
|
+
**sort** | [**List[str]**](str.md)| Sorting criteria in the format: property(,asc|desc). Default sort order is ascending. Multiple sort criteria are supported. | [optional] [default to [name,ASC]]
|
|
195
|
+
|
|
196
|
+
### Return type
|
|
197
|
+
|
|
198
|
+
[**PageStatusDto**](PageStatusDto.md)
|
|
199
|
+
|
|
200
|
+
### Authorization
|
|
201
|
+
|
|
202
|
+
No authorization required
|
|
203
|
+
|
|
204
|
+
### HTTP request headers
|
|
205
|
+
|
|
206
|
+
- **Content-Type**: Not defined
|
|
207
|
+
- **Accept**: */*
|
|
208
|
+
|
|
209
|
+
### HTTP response details
|
|
210
|
+
|
|
211
|
+
| Status code | Description | Response headers |
|
|
212
|
+
|-------------|-------------|------------------|
|
|
213
|
+
**200** | OK | - |
|
|
214
|
+
|
|
215
|
+
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
|
216
|
+
|
|
217
|
+
# **find_one14**
|
|
218
|
+
> StatusDto find_one14(id)
|
|
219
|
+
|
|
220
|
+
Find status by id
|
|
221
|
+
|
|
222
|
+
### Example
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
import src.client.generated
|
|
227
|
+
from src.client.generated.models.status_dto import StatusDto
|
|
228
|
+
from src.client.generated.rest import ApiException
|
|
229
|
+
from pprint import pprint
|
|
230
|
+
|
|
231
|
+
# Defining the host is optional and defaults to http://localhost
|
|
232
|
+
# See configuration.py for a list of all supported configuration parameters.
|
|
233
|
+
configuration = src.client.generated.Configuration(
|
|
234
|
+
host = "http://localhost"
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
# Enter a context with an instance of the API client
|
|
239
|
+
async with src.client.generated.ApiClient(configuration) as api_client:
|
|
240
|
+
# Create an instance of the API class
|
|
241
|
+
api_instance = src.client.generated.StatusControllerApi(api_client)
|
|
242
|
+
id = 56 # int |
|
|
243
|
+
|
|
244
|
+
try:
|
|
245
|
+
# Find status by id
|
|
246
|
+
api_response = await api_instance.find_one14(id)
|
|
247
|
+
print("The response of StatusControllerApi->find_one14:\n")
|
|
248
|
+
pprint(api_response)
|
|
249
|
+
except Exception as e:
|
|
250
|
+
print("Exception when calling StatusControllerApi->find_one14: %s\n" % e)
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
### Parameters
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
Name | Type | Description | Notes
|
|
259
|
+
------------- | ------------- | ------------- | -------------
|
|
260
|
+
**id** | **int**| |
|
|
261
|
+
|
|
262
|
+
### Return type
|
|
263
|
+
|
|
264
|
+
[**StatusDto**](StatusDto.md)
|
|
265
|
+
|
|
266
|
+
### Authorization
|
|
267
|
+
|
|
268
|
+
No authorization required
|
|
269
|
+
|
|
270
|
+
### HTTP request headers
|
|
271
|
+
|
|
272
|
+
- **Content-Type**: Not defined
|
|
273
|
+
- **Accept**: */*
|
|
274
|
+
|
|
275
|
+
### HTTP response details
|
|
276
|
+
|
|
277
|
+
| Status code | Description | Response headers |
|
|
278
|
+
|-------------|-------------|------------------|
|
|
279
|
+
**200** | OK | - |
|
|
280
|
+
|
|
281
|
+
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
|
282
|
+
|
|
283
|
+
# **patch17**
|
|
284
|
+
> StatusDto patch17(id, status_patch_dto)
|
|
285
|
+
|
|
286
|
+
Patch status
|
|
287
|
+
|
|
288
|
+
### Example
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
import src.client.generated
|
|
293
|
+
from src.client.generated.models.status_dto import StatusDto
|
|
294
|
+
from src.client.generated.models.status_patch_dto import StatusPatchDto
|
|
295
|
+
from src.client.generated.rest import ApiException
|
|
296
|
+
from pprint import pprint
|
|
297
|
+
|
|
298
|
+
# Defining the host is optional and defaults to http://localhost
|
|
299
|
+
# See configuration.py for a list of all supported configuration parameters.
|
|
300
|
+
configuration = src.client.generated.Configuration(
|
|
301
|
+
host = "http://localhost"
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
# Enter a context with an instance of the API client
|
|
306
|
+
async with src.client.generated.ApiClient(configuration) as api_client:
|
|
307
|
+
# Create an instance of the API class
|
|
308
|
+
api_instance = src.client.generated.StatusControllerApi(api_client)
|
|
309
|
+
id = 56 # int |
|
|
310
|
+
status_patch_dto = src.client.generated.StatusPatchDto() # StatusPatchDto |
|
|
311
|
+
|
|
312
|
+
try:
|
|
313
|
+
# Patch status
|
|
314
|
+
api_response = await api_instance.patch17(id, status_patch_dto)
|
|
315
|
+
print("The response of StatusControllerApi->patch17:\n")
|
|
316
|
+
pprint(api_response)
|
|
317
|
+
except Exception as e:
|
|
318
|
+
print("Exception when calling StatusControllerApi->patch17: %s\n" % e)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
### Parameters
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
Name | Type | Description | Notes
|
|
327
|
+
------------- | ------------- | ------------- | -------------
|
|
328
|
+
**id** | **int**| |
|
|
329
|
+
**status_patch_dto** | [**StatusPatchDto**](StatusPatchDto.md)| |
|
|
330
|
+
|
|
331
|
+
### Return type
|
|
332
|
+
|
|
333
|
+
[**StatusDto**](StatusDto.md)
|
|
334
|
+
|
|
335
|
+
### Authorization
|
|
336
|
+
|
|
337
|
+
No authorization required
|
|
338
|
+
|
|
339
|
+
### HTTP request headers
|
|
340
|
+
|
|
341
|
+
- **Content-Type**: application/json
|
|
342
|
+
- **Accept**: */*
|
|
343
|
+
|
|
344
|
+
### HTTP response details
|
|
345
|
+
|
|
346
|
+
| Status code | Description | Response headers |
|
|
347
|
+
|-------------|-------------|------------------|
|
|
348
|
+
**200** | OK | - |
|
|
349
|
+
|
|
350
|
+
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
|
351
|
+
|
|
352
|
+
# **suggest8**
|
|
353
|
+
> PageIdAndNameOnlyDto suggest8(query=query, workflow_id=workflow_id, id=id, ignore_id=ignore_id, page=page, size=size, sort=sort)
|
|
354
|
+
|
|
355
|
+
Suggest statuses
|
|
356
|
+
|
|
357
|
+
### Example
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
```python
|
|
361
|
+
import src.client.generated
|
|
362
|
+
from src.client.generated.models.page_id_and_name_only_dto import PageIdAndNameOnlyDto
|
|
363
|
+
from src.client.generated.rest import ApiException
|
|
364
|
+
from pprint import pprint
|
|
365
|
+
|
|
366
|
+
# Defining the host is optional and defaults to http://localhost
|
|
367
|
+
# See configuration.py for a list of all supported configuration parameters.
|
|
368
|
+
configuration = src.client.generated.Configuration(
|
|
369
|
+
host = "http://localhost"
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
# Enter a context with an instance of the API client
|
|
374
|
+
async with src.client.generated.ApiClient(configuration) as api_client:
|
|
375
|
+
# Create an instance of the API class
|
|
376
|
+
api_instance = src.client.generated.StatusControllerApi(api_client)
|
|
377
|
+
query = 'query_example' # str | (optional)
|
|
378
|
+
workflow_id = 56 # int | (optional)
|
|
379
|
+
id = [56] # List[int] | (optional)
|
|
380
|
+
ignore_id = [56] # List[int] | (optional)
|
|
381
|
+
page = 0 # int | Zero-based page index (0..N) (optional) (default to 0)
|
|
382
|
+
size = 10 # int | The size of the page to be returned (optional) (default to 10)
|
|
383
|
+
sort = [name,ASC] # List[str] | Sorting criteria in the format: property(,asc|desc). Default sort order is ascending. Multiple sort criteria are supported. (optional) (default to [name,ASC])
|
|
384
|
+
|
|
385
|
+
try:
|
|
386
|
+
# Suggest statuses
|
|
387
|
+
api_response = await api_instance.suggest8(query=query, workflow_id=workflow_id, id=id, ignore_id=ignore_id, page=page, size=size, sort=sort)
|
|
388
|
+
print("The response of StatusControllerApi->suggest8:\n")
|
|
389
|
+
pprint(api_response)
|
|
390
|
+
except Exception as e:
|
|
391
|
+
print("Exception when calling StatusControllerApi->suggest8: %s\n" % e)
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
### Parameters
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
Name | Type | Description | Notes
|
|
400
|
+
------------- | ------------- | ------------- | -------------
|
|
401
|
+
**query** | **str**| | [optional]
|
|
402
|
+
**workflow_id** | **int**| | [optional]
|
|
403
|
+
**id** | [**List[int]**](int.md)| | [optional]
|
|
404
|
+
**ignore_id** | [**List[int]**](int.md)| | [optional]
|
|
405
|
+
**page** | **int**| Zero-based page index (0..N) | [optional] [default to 0]
|
|
406
|
+
**size** | **int**| The size of the page to be returned | [optional] [default to 10]
|
|
407
|
+
**sort** | [**List[str]**](str.md)| Sorting criteria in the format: property(,asc|desc). Default sort order is ascending. Multiple sort criteria are supported. | [optional] [default to [name,ASC]]
|
|
408
|
+
|
|
409
|
+
### Return type
|
|
410
|
+
|
|
411
|
+
[**PageIdAndNameOnlyDto**](PageIdAndNameOnlyDto.md)
|
|
412
|
+
|
|
413
|
+
### Authorization
|
|
414
|
+
|
|
415
|
+
No authorization required
|
|
416
|
+
|
|
417
|
+
### HTTP request headers
|
|
418
|
+
|
|
419
|
+
- **Content-Type**: Not defined
|
|
420
|
+
- **Accept**: */*
|
|
421
|
+
|
|
422
|
+
### HTTP response details
|
|
423
|
+
|
|
424
|
+
| Status code | Description | Response headers |
|
|
425
|
+
|-------------|-------------|------------------|
|
|
426
|
+
**200** | OK | - |
|
|
427
|
+
|
|
428
|
+
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
|
429
|
+
|
|
@@ -19,7 +19,6 @@ from src.client.generated.models import (
|
|
|
19
19
|
TestCaseCreateV2Dto,
|
|
20
20
|
TestCaseDto,
|
|
21
21
|
TestCaseOverviewDto,
|
|
22
|
-
TestCaseTreeSelectionDto,
|
|
23
22
|
TestTagDto,
|
|
24
23
|
)
|
|
25
24
|
from src.client.generated.models.attachment_step_dto import AttachmentStepDto
|
|
@@ -80,9 +79,10 @@ class TestCaseService:
|
|
|
80
79
|
self._client = client
|
|
81
80
|
self._project_id = client.get_project()
|
|
82
81
|
self._attachment_service = attachment_service or AttachmentService(self._client)
|
|
83
|
-
|
|
82
|
+
# {project_id: {name: {"id": int, "values": list[str]}}}
|
|
83
|
+
self._cf_cache: dict[int, dict[str, dict[str, Any]]] = {}
|
|
84
84
|
|
|
85
|
-
async def create_test_case(
|
|
85
|
+
async def create_test_case( # noqa: C901
|
|
86
86
|
self,
|
|
87
87
|
name: str,
|
|
88
88
|
description: str | None = None,
|
|
@@ -120,15 +120,46 @@ class TestCaseService:
|
|
|
120
120
|
resolved_custom_fields = []
|
|
121
121
|
if custom_fields:
|
|
122
122
|
project_cfs = await self._get_resolved_custom_fields(self._project_id)
|
|
123
|
+
missing_fields = []
|
|
124
|
+
invalid_values = []
|
|
125
|
+
|
|
123
126
|
for key, value in custom_fields.items():
|
|
124
|
-
|
|
125
|
-
if
|
|
126
|
-
|
|
127
|
+
cf_info = project_cfs.get(key)
|
|
128
|
+
if cf_info is None:
|
|
129
|
+
missing_fields.append(key)
|
|
130
|
+
else:
|
|
131
|
+
cf_id = cf_info["id"]
|
|
132
|
+
allowed_values = cf_info["values"]
|
|
133
|
+
|
|
134
|
+
# Validate value if allowed_values are present
|
|
135
|
+
if allowed_values and value not in allowed_values:
|
|
136
|
+
invalid_values.append(f"'{key}': '{value}' (Allowed: {', '.join(allowed_values)})")
|
|
137
|
+
else:
|
|
138
|
+
resolved_custom_fields.append(
|
|
139
|
+
CustomFieldValueWithCfDto(custom_field=CustomFieldDto(id=cf_id, name=key), name=value)
|
|
140
|
+
)
|
|
127
141
|
|
|
128
|
-
|
|
129
|
-
|
|
142
|
+
error_messages = []
|
|
143
|
+
|
|
144
|
+
if missing_fields:
|
|
145
|
+
missing_list_str = "\n".join([f"- {name}" for name in missing_fields])
|
|
146
|
+
error_messages.append(
|
|
147
|
+
f"The following custom fields were not found in project {self._project_id}:\n{missing_list_str}"
|
|
130
148
|
)
|
|
131
149
|
|
|
150
|
+
if invalid_values:
|
|
151
|
+
invalid_list_str = "\n".join([f"- {item}" for item in invalid_values])
|
|
152
|
+
error_messages.append(f"The following custom field values are invalid:\n{invalid_list_str}")
|
|
153
|
+
|
|
154
|
+
if error_messages:
|
|
155
|
+
full_error_msg = "\n\n".join(error_messages) + (
|
|
156
|
+
"\n\nUsage Hint:\n"
|
|
157
|
+
"1. Exclude all missing custom fields from your request.\n"
|
|
158
|
+
"2. Correct any invalid values to match the allowed options.\n"
|
|
159
|
+
"3. Only include fields that explicitly exist in the project configuration."
|
|
160
|
+
)
|
|
161
|
+
raise AllureValidationError(full_error_msg)
|
|
162
|
+
|
|
132
163
|
# 3. Create TestCaseCreateV2Dto with validation
|
|
133
164
|
tag_dtos = self._build_tag_dtos(tags)
|
|
134
165
|
try:
|
|
@@ -185,6 +216,27 @@ class TestCaseService:
|
|
|
185
216
|
"""
|
|
186
217
|
return await self._client.get_test_case(test_case_id)
|
|
187
218
|
|
|
219
|
+
async def get_custom_fields(self, name: str | None = None) -> list[dict[str, Any]]:
|
|
220
|
+
"""Get custom fields for the project with optional name filtering.
|
|
221
|
+
|
|
222
|
+
This method uses the internal cache to avoid duplicate API calls when
|
|
223
|
+
both get_custom_fields and create_test_case are used in the same session.
|
|
224
|
+
"""
|
|
225
|
+
# Use cached resolution method to get field mapping
|
|
226
|
+
cf_mapping = await self._get_resolved_custom_fields(self._project_id)
|
|
227
|
+
|
|
228
|
+
result = []
|
|
229
|
+
filter_name = name.lower() if name else None
|
|
230
|
+
|
|
231
|
+
# Convert the cached mapping back to the display format
|
|
232
|
+
for field_name, field_info in cf_mapping.items():
|
|
233
|
+
if filter_name and filter_name not in field_name.lower():
|
|
234
|
+
continue
|
|
235
|
+
|
|
236
|
+
result.append({"name": field_name, "required": field_info["required"], "values": field_info["values"]})
|
|
237
|
+
|
|
238
|
+
return result
|
|
239
|
+
|
|
188
240
|
async def update_test_case(self, test_case_id: int, data: TestCaseUpdate) -> TestCaseDto:
|
|
189
241
|
"""Update an existing test case.
|
|
190
242
|
|
|
@@ -423,10 +475,17 @@ class TestCaseService:
|
|
|
423
475
|
resolved_cfs = []
|
|
424
476
|
project_cfs = await self._get_resolved_custom_fields(project_id)
|
|
425
477
|
for key, value in data.custom_fields.items():
|
|
426
|
-
|
|
427
|
-
if
|
|
478
|
+
cf_info = project_cfs.get(key)
|
|
479
|
+
if cf_info:
|
|
480
|
+
# For updates, we blindly trust if it exists, or should we validate?
|
|
481
|
+
# The plan implies validating create, let's also validate update to be safe,
|
|
482
|
+
# but typically update is just "prepare kwargs".
|
|
483
|
+
# If we want validation here, we should add it.
|
|
484
|
+
# For now, adapting to the new dict structure is required.
|
|
428
485
|
resolved_cfs.append(
|
|
429
|
-
CustomFieldValueWithCfDto(
|
|
486
|
+
CustomFieldValueWithCfDto(
|
|
487
|
+
custom_field=CustomFieldDto(id=cf_info["id"], name=key), name=value
|
|
488
|
+
)
|
|
430
489
|
)
|
|
431
490
|
patch_kwargs["custom_fields"] = resolved_cfs
|
|
432
491
|
has_changes = True
|
|
@@ -695,31 +754,31 @@ class TestCaseService:
|
|
|
695
754
|
raise AllureValidationError(f"Invalid tag '{t}': {e}", suggestions=[hint]) from e
|
|
696
755
|
return tag_dtos
|
|
697
756
|
|
|
698
|
-
async def _get_resolved_custom_fields(self, project_id: int) -> dict[str,
|
|
699
|
-
"""Get or fetch custom field name-to-
|
|
757
|
+
async def _get_resolved_custom_fields(self, project_id: int) -> dict[str, dict[str, Any]]:
|
|
758
|
+
"""Get or fetch custom field name-to-info mapping for a project."""
|
|
700
759
|
if project_id in self._cf_cache:
|
|
701
760
|
return self._cf_cache[project_id]
|
|
702
761
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
762
|
+
# Use the client wrapper method for consistent error handling and response processing
|
|
763
|
+
cfs = await self._client.get_custom_fields_with_values(project_id)
|
|
764
|
+
logger.debug("Fetched %d custom fields for project %d", len(cfs), project_id)
|
|
765
|
+
mapping = {}
|
|
766
|
+
for cf_with_values in cfs:
|
|
767
|
+
if cf_with_values.custom_field and cf_with_values.custom_field.custom_field:
|
|
768
|
+
inner_cf = cf_with_values.custom_field.custom_field
|
|
769
|
+
if inner_cf.name and inner_cf.id:
|
|
770
|
+
values = []
|
|
771
|
+
if cf_with_values.values:
|
|
772
|
+
values = [v.name for v in cf_with_values.values if v.name]
|
|
773
|
+
|
|
774
|
+
mapping[inner_cf.name] = {
|
|
775
|
+
"id": inner_cf.id,
|
|
776
|
+
"required": bool(cf_with_values.custom_field.required),
|
|
777
|
+
"values": values,
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
self._cf_cache[project_id] = mapping
|
|
781
|
+
return mapping
|
|
723
782
|
|
|
724
783
|
def _build_custom_field_dtos(self, custom_fields: dict[str, str] | None) -> list[CustomFieldValueWithCfDto]:
|
|
725
784
|
"""DEPRECATED: Use inline resolution in create_test_case."""
|
src/tools/__init__.py
CHANGED
|
@@ -2,6 +2,7 @@ from collections.abc import Awaitable, Callable
|
|
|
2
2
|
|
|
3
3
|
from src.tools.create_test_case import create_test_case
|
|
4
4
|
from src.tools.delete_test_case import delete_test_case
|
|
5
|
+
from src.tools.get_custom_fields import get_custom_fields
|
|
5
6
|
from src.tools.link_shared_step import link_shared_step
|
|
6
7
|
from src.tools.search import get_test_case_details, list_test_cases, search_test_cases
|
|
7
8
|
from src.tools.shared_steps import create_shared_step, delete_shared_step, list_shared_steps, update_shared_step
|
|
@@ -13,6 +14,7 @@ __all__ = [
|
|
|
13
14
|
"create_test_case",
|
|
14
15
|
"delete_shared_step",
|
|
15
16
|
"delete_test_case",
|
|
17
|
+
"get_custom_fields",
|
|
16
18
|
"get_test_case_details",
|
|
17
19
|
"link_shared_step",
|
|
18
20
|
"list_shared_steps",
|
|
@@ -31,6 +33,7 @@ all_tools: list[ToolFn] = [
|
|
|
31
33
|
update_test_case,
|
|
32
34
|
delete_test_case,
|
|
33
35
|
list_test_cases,
|
|
36
|
+
get_custom_fields,
|
|
34
37
|
search_test_cases,
|
|
35
38
|
create_shared_step,
|
|
36
39
|
list_shared_steps,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
5
|
+
from src.client import AllureClient
|
|
6
|
+
from src.services.test_case_service import TestCaseService
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def get_custom_fields(
|
|
10
|
+
name: Annotated[
|
|
11
|
+
str | None, Field(description="Optional case-insensitive name filter to search for specific custom fields.")
|
|
12
|
+
] = None,
|
|
13
|
+
project_id: Annotated[
|
|
14
|
+
int | None, Field(description="Allure TestOps project ID to fetch custom fields from.")
|
|
15
|
+
] = None,
|
|
16
|
+
) -> str:
|
|
17
|
+
"""Get available custom fields and their allowed values for the project.
|
|
18
|
+
|
|
19
|
+
Use this tool to discover what custom fields are available (e.g., 'Layer', 'Priority')
|
|
20
|
+
and what values are valid for them (e.g., 'UI', 'High'). This is essential before
|
|
21
|
+
creating or updating test cases to ensure you use valid field names and values.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
name: Optional name filter to find a specific field (case-insensitive).
|
|
25
|
+
project_id: Optional project ID override.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
A list of custom fields with their required status and allowed values.
|
|
29
|
+
"""
|
|
30
|
+
async with AllureClient.from_env(project=project_id) as client:
|
|
31
|
+
service = TestCaseService(client)
|
|
32
|
+
fields = await service.get_custom_fields(name=name)
|
|
33
|
+
|
|
34
|
+
if not fields:
|
|
35
|
+
if name:
|
|
36
|
+
return f"No custom fields found matching '{name}'."
|
|
37
|
+
return "No custom fields found for this project."
|
|
38
|
+
|
|
39
|
+
lines = [f"Found {len(fields)} custom fields:"]
|
|
40
|
+
|
|
41
|
+
for cf in fields:
|
|
42
|
+
field_name = cf["name"]
|
|
43
|
+
required = "required" if cf["required"] else "optional"
|
|
44
|
+
values = ", ".join(cf["values"]) if cf["values"] else "Any text/No allowed values"
|
|
45
|
+
|
|
46
|
+
lines.append(f"- {field_name} ({required}): {values}")
|
|
47
|
+
|
|
48
|
+
return "\n".join(lines)
|