@testomatio/mcp 2.0.0 → 2.0.1-beta.1
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.
- package/README.md +76 -13
- package/package.json +3 -2
- package/src/api/http-client.js +12 -3
- package/src/api/testomatio-client.js +89 -8
- package/src/index.js +23 -2
- package/src/mcp/configs/entity-crud-config.js +12 -4
- package/src/mcp/definitions/labels.js +0 -25
- package/src/mcp/definitions/milestones.js +43 -0
- package/src/mcp/definitions/plans.js +38 -2
- package/src/mcp/definitions/requirements.js +163 -0
- package/src/mcp/definitions/rungroups.js +0 -25
- package/src/mcp/definitions/runs.js +18 -18
- package/src/mcp/definitions/snippets.js +2 -25
- package/src/mcp/definitions/steps.js +2 -25
- package/src/mcp/definitions/suites.js +6 -8
- package/src/mcp/definitions/testruns.js +194 -2
- package/src/mcp/definitions/tests.js +12 -36
- package/src/mcp/definitions/tql-reference.js +133 -0
- package/src/mcp/registry/handlers.js +17 -2
- package/src/mcp/registry/listings.js +101 -43
- package/src/mcp/registry/payloads.js +89 -8
- package/src/mcp/server.js +47 -4
- package/src/mcp/tool-definitions.js +4 -0
- package/src/mcp/tool-registry.js +7 -2
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { TESTS_TQL_INPUT_DESCRIPTION, TESTS_TQL_REFERENCE } from './tql-reference.js';
|
|
2
|
+
|
|
1
3
|
export const TESTS_TOOLS = [
|
|
2
4
|
{
|
|
3
5
|
"name": "tests_list",
|
|
4
|
-
"description":
|
|
6
|
+
"description": `List tests (/api/v2/{project_id}/tests). ${TESTS_TQL_REFERENCE}`,
|
|
5
7
|
"inputSchema": {
|
|
6
8
|
"type": "object",
|
|
7
9
|
"properties": {
|
|
@@ -14,23 +16,9 @@ export const TESTS_TOOLS = [
|
|
|
14
16
|
"minimum": 1,
|
|
15
17
|
"maximum": 100
|
|
16
18
|
},
|
|
17
|
-
"
|
|
18
|
-
"type": "string"
|
|
19
|
-
|
|
20
|
-
"search_text": {
|
|
21
|
-
"type": "string"
|
|
22
|
-
},
|
|
23
|
-
"query": {
|
|
24
|
-
"type": "string"
|
|
25
|
-
},
|
|
26
|
-
"assigned_to": {
|
|
27
|
-
"type": "string"
|
|
28
|
-
},
|
|
29
|
-
"priority": {
|
|
30
|
-
"type": "string"
|
|
31
|
-
},
|
|
32
|
-
"state": {
|
|
33
|
-
"type": "string"
|
|
19
|
+
"tql": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": TESTS_TQL_INPUT_DESCRIPTION
|
|
34
22
|
}
|
|
35
23
|
},
|
|
36
24
|
"additionalProperties": false
|
|
@@ -100,6 +88,7 @@ export const TESTS_TOOLS = [
|
|
|
100
88
|
"label",
|
|
101
89
|
"custom_field",
|
|
102
90
|
"tag",
|
|
91
|
+
"milestone",
|
|
103
92
|
"issue",
|
|
104
93
|
"jira"
|
|
105
94
|
]
|
|
@@ -178,6 +167,7 @@ export const TESTS_TOOLS = [
|
|
|
178
167
|
"label",
|
|
179
168
|
"custom_field",
|
|
180
169
|
"tag",
|
|
170
|
+
"milestone",
|
|
181
171
|
"issue",
|
|
182
172
|
"jira"
|
|
183
173
|
]
|
|
@@ -219,16 +209,10 @@ export const TESTS_TOOLS = [
|
|
|
219
209
|
},
|
|
220
210
|
{
|
|
221
211
|
"name": "tests_search",
|
|
222
|
-
"description":
|
|
212
|
+
"description": `Search tests using TQL (delegates to tests_list). ${TESTS_TQL_REFERENCE}`,
|
|
223
213
|
"inputSchema": {
|
|
224
214
|
"type": "object",
|
|
225
215
|
"properties": {
|
|
226
|
-
"query": {
|
|
227
|
-
"type": "string"
|
|
228
|
-
},
|
|
229
|
-
"search_text": {
|
|
230
|
-
"type": "string"
|
|
231
|
-
},
|
|
232
216
|
"page": {
|
|
233
217
|
"type": "integer",
|
|
234
218
|
"minimum": 1
|
|
@@ -238,17 +222,9 @@ export const TESTS_TOOLS = [
|
|
|
238
222
|
"minimum": 1,
|
|
239
223
|
"maximum": 100
|
|
240
224
|
},
|
|
241
|
-
"
|
|
242
|
-
"type": "string"
|
|
243
|
-
|
|
244
|
-
"assigned_to": {
|
|
245
|
-
"type": "string"
|
|
246
|
-
},
|
|
247
|
-
"priority": {
|
|
248
|
-
"type": "string"
|
|
249
|
-
},
|
|
250
|
-
"state": {
|
|
251
|
-
"type": "string"
|
|
225
|
+
"tql": {
|
|
226
|
+
"type": "string",
|
|
227
|
+
"description": TESTS_TQL_INPUT_DESCRIPTION
|
|
252
228
|
}
|
|
253
229
|
},
|
|
254
230
|
"additionalProperties": false
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
const TESTS_TQL_VARIABLES = [
|
|
2
|
+
'tag',
|
|
3
|
+
'label',
|
|
4
|
+
'priority',
|
|
5
|
+
'issue',
|
|
6
|
+
'jira',
|
|
7
|
+
'state',
|
|
8
|
+
'status',
|
|
9
|
+
'custom_status',
|
|
10
|
+
'created_at',
|
|
11
|
+
'updated_at',
|
|
12
|
+
'last_run_at',
|
|
13
|
+
'executed_at',
|
|
14
|
+
'created_by',
|
|
15
|
+
'assigned_to',
|
|
16
|
+
'suite',
|
|
17
|
+
'test',
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const RUNS_TQL_VARIABLES = [
|
|
21
|
+
'title',
|
|
22
|
+
'plan',
|
|
23
|
+
'rungroup',
|
|
24
|
+
'env',
|
|
25
|
+
'tag',
|
|
26
|
+
'label',
|
|
27
|
+
'jira',
|
|
28
|
+
'duration',
|
|
29
|
+
'passed_count',
|
|
30
|
+
'failed_count',
|
|
31
|
+
'skipped_count',
|
|
32
|
+
'automated',
|
|
33
|
+
'manual',
|
|
34
|
+
'mixed',
|
|
35
|
+
'finished',
|
|
36
|
+
'unfinished',
|
|
37
|
+
'passed',
|
|
38
|
+
'failed',
|
|
39
|
+
'terminated',
|
|
40
|
+
'published',
|
|
41
|
+
'private',
|
|
42
|
+
'archived',
|
|
43
|
+
'unarchived',
|
|
44
|
+
'with_defect',
|
|
45
|
+
'has_defect',
|
|
46
|
+
'has_test',
|
|
47
|
+
'has_test_tag',
|
|
48
|
+
'has_test_label',
|
|
49
|
+
'has_suite',
|
|
50
|
+
'has_message',
|
|
51
|
+
'has_custom_status',
|
|
52
|
+
'has_assigned_to',
|
|
53
|
+
'has_retries',
|
|
54
|
+
'has_test_duration',
|
|
55
|
+
'has_priority',
|
|
56
|
+
'created_at',
|
|
57
|
+
'updated_at',
|
|
58
|
+
'launched_at',
|
|
59
|
+
'finished_at',
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const TESTS_TQL_EXAMPLES = [
|
|
63
|
+
"priority == 'high'",
|
|
64
|
+
"state == 'automated'",
|
|
65
|
+
"tag in ['smoke', 'stage1'] and status == 'failed'",
|
|
66
|
+
"suite % 'Checkout'",
|
|
67
|
+
"test % 'User login'",
|
|
68
|
+
"created_at < 1.month_ago",
|
|
69
|
+
"jira in ['JST-1', 'JST-2']",
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const RUNS_TQL_EXAMPLES = [
|
|
73
|
+
"title % 'Manual tests'",
|
|
74
|
+
"plan == '{PLAN_ID}'",
|
|
75
|
+
"env in ['Windows', 'Linux']",
|
|
76
|
+
"failed and has_test_tag == 'regression'",
|
|
77
|
+
'finished and with_defect',
|
|
78
|
+
'has_retries > 2',
|
|
79
|
+
"automated and env == 'Production' and has_message % 'Server Error'",
|
|
80
|
+
"finished_at >= '2025-07-01' and finished_at <= '2025-07-31' and failed",
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
const COMMON_TQL_SYNTAX =
|
|
84
|
+
"Supported syntax includes `==`, `!=`, `>`, `<`, `>=`, `<=`, `in [...]`, `%` for partial text match, `and`, `or`, `not`, and parentheses for grouping. Use quotes for string values, for example `state == 'automated'`.";
|
|
85
|
+
|
|
86
|
+
export const TESTS_TQL_REFERENCE =
|
|
87
|
+
`TQL (Testomat.io Query Language) is a string expression passed in \`tql\` to filter tests. ${COMMON_TQL_SYNTAX} ` +
|
|
88
|
+
`Documented test variables: ${TESTS_TQL_VARIABLES.map((item) => `\`${item}\``).join(', ')}. ` +
|
|
89
|
+
`Documented examples: ${TESTS_TQL_EXAMPLES.map((item) => `\`${item}\``).join(', ')}. ` +
|
|
90
|
+
'Do not invent undocumented fields or syntax. If a query fails, simplify it to one documented predicate.';
|
|
91
|
+
|
|
92
|
+
export const TESTS_TQL_INPUT_DESCRIPTION =
|
|
93
|
+
`TQL filter for tests. Documented variables: ${TESTS_TQL_VARIABLES.map((item) => `\`${item}\``).join(', ')}. ` +
|
|
94
|
+
`Examples: ${TESTS_TQL_EXAMPLES.map((item) => `\`${item}\``).join(', ')}.`;
|
|
95
|
+
|
|
96
|
+
export const RUNS_TQL_REFERENCE =
|
|
97
|
+
`TQL (Testomat.io Query Language) is a string expression passed in \`tql\` to filter runs. ${COMMON_TQL_SYNTAX} ` +
|
|
98
|
+
'Runs also support boolean flags without comparison such as `failed`, `finished`, `automated`, or `with_defect`. ' +
|
|
99
|
+
`Documented run variables: ${RUNS_TQL_VARIABLES.map((item) => `\`${item}\``).join(', ')}. ` +
|
|
100
|
+
`Documented examples: ${RUNS_TQL_EXAMPLES.map((item) => `\`${item}\``).join(', ')}. ` +
|
|
101
|
+
'Do not invent undocumented fields or syntax. If a query fails, simplify it to one documented predicate.';
|
|
102
|
+
|
|
103
|
+
export const RUNS_TQL_INPUT_DESCRIPTION =
|
|
104
|
+
`TQL filter for runs. Documented variables: ${RUNS_TQL_VARIABLES.map((item) => `\`${item}\``).join(', ')}. ` +
|
|
105
|
+
`Examples: ${RUNS_TQL_EXAMPLES.map((item) => `\`${item}\``).join(', ')}.`;
|
|
106
|
+
|
|
107
|
+
export const ANALYTICS_TESTS_TQL_REFERENCE =
|
|
108
|
+
`TQL (Testomat.io Query Language) is a string expression passed in \`q\` to filter enterprise analytics test reports. ${COMMON_TQL_SYNTAX} ` +
|
|
109
|
+
'For analytics tools, the API parameter name is `q`, not `tql`. ' +
|
|
110
|
+
`Documented analytics test variables: ${TESTS_TQL_VARIABLES.map((item) => `\`${item}\``).join(', ')}. ` +
|
|
111
|
+
`Documented examples: ${TESTS_TQL_EXAMPLES.map((item) => `\`${item}\``).join(', ')}. ` +
|
|
112
|
+
'Do not invent undocumented fields or syntax. If a query fails, simplify it to one documented predicate.';
|
|
113
|
+
|
|
114
|
+
export const ANALYTICS_TESTS_TQL_INPUT_DESCRIPTION =
|
|
115
|
+
'TQL filter for analytics test reports. The API parameter name is `q`, not `tql`. ' +
|
|
116
|
+
`Documented variables: ${TESTS_TQL_VARIABLES.map((item) => `\`${item}\``).join(', ')}. ` +
|
|
117
|
+
`Examples: ${TESTS_TQL_EXAMPLES.map((item) => `\`${item}\``).join(', ')}.`;
|
|
118
|
+
|
|
119
|
+
export const ANALYTICS_STATS_TQL_REFERENCE =
|
|
120
|
+
`TQL (Testomat.io Query Language) is a string expression passed in \`q\` to filter enterprise analytics aggregated reports. ${COMMON_TQL_SYNTAX} ` +
|
|
121
|
+
'For analytics tools, the API parameter name is `q`, not `tql`. ' +
|
|
122
|
+
'According to the official Analytics docs, analytics queries are configured using supported query variables for two data sources: `Tests Variables` and `Runs Variables`. ' +
|
|
123
|
+
`Documented Tests Variables: ${TESTS_TQL_VARIABLES.map((item) => `\`${item}\``).join(', ')}. ` +
|
|
124
|
+
`Documented Runs Variables: ${RUNS_TQL_VARIABLES.map((item) => `\`${item}\``).join(', ')}. ` +
|
|
125
|
+
`Documented Tests examples: ${TESTS_TQL_EXAMPLES.map((item) => `\`${item}\``).join(', ')}. ` +
|
|
126
|
+
`Documented Runs examples: ${RUNS_TQL_EXAMPLES.map((item) => `\`${item}\``).join(', ')}. ` +
|
|
127
|
+
'Use Tests Variables for test-centric filters and Runs Variables for run-centric filters such as `plan`, `rungroup`, `env`, `finished_at`, or `has_test_tag`. Do not invent undocumented fields or syntax. If a query fails, simplify it to one documented predicate.';
|
|
128
|
+
|
|
129
|
+
export const ANALYTICS_STATS_TQL_INPUT_DESCRIPTION =
|
|
130
|
+
'TQL filter for analytics aggregated reports. The API parameter name is `q`, not `tql`. ' +
|
|
131
|
+
`Documented Tests Variables: ${TESTS_TQL_VARIABLES.map((item) => `\`${item}\``).join(', ')}. ` +
|
|
132
|
+
`Documented Runs Variables: ${RUNS_TQL_VARIABLES.map((item) => `\`${item}\``).join(', ')}. ` +
|
|
133
|
+
`Examples: ${TESTS_TQL_EXAMPLES.map((item) => `\`${item}\``).join(', ')}, ${RUNS_TQL_EXAMPLES.map((item) => `\`${item}\``).join(', ')}.`;
|
|
@@ -8,8 +8,10 @@ export const handlerMethods = {
|
|
|
8
8
|
|
|
9
9
|
handlers[`${toolPrefix}_list`] = async (args = {}) =>
|
|
10
10
|
this.asText(await this[listMethod](args));
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
if (searchMethod) {
|
|
12
|
+
handlers[`${toolPrefix}_search`] = async (args = {}) =>
|
|
13
|
+
this.asText(await this[searchMethod](args));
|
|
14
|
+
}
|
|
13
15
|
handlers[`${toolPrefix}_get`] = async (args = {}) =>
|
|
14
16
|
this.asText(await this.apiClient.get(resource, this.pickRequiredArg(args, idArg)));
|
|
15
17
|
handlers[`${toolPrefix}_create`] = async (args = {}) =>
|
|
@@ -57,6 +59,10 @@ export const handlerMethods = {
|
|
|
57
59
|
handlers.tags_get = async ({ tag_id: tagId }) => this.asText(await this.getTagByTitle(tagId));
|
|
58
60
|
handlers.tags_search = async (args = {}) => this.asText(await this.searchTags(args));
|
|
59
61
|
|
|
62
|
+
handlers.milestones_list = async (args = {}) => this.asText(await this.listMilestones(args));
|
|
63
|
+
handlers.milestones_get = async ({ milestone_id: milestoneId }) =>
|
|
64
|
+
this.asText(await this.apiClient.get('milestones', milestoneId));
|
|
65
|
+
|
|
60
66
|
handlers.issues_list = async (args = {}) => this.asText(await this.listIssues(args));
|
|
61
67
|
handlers.issues_search = async (args = {}) => this.asText(await this.searchIssues(args));
|
|
62
68
|
handlers.issues_create = async (args = {}) => this.asText(await this.createIssue(args));
|
|
@@ -82,6 +88,9 @@ export const handlerMethods = {
|
|
|
82
88
|
if (spec.createMode === 'run') {
|
|
83
89
|
return this.createRunWithFallback(args);
|
|
84
90
|
}
|
|
91
|
+
if (spec.createMode === 'requirement') {
|
|
92
|
+
return this.createRequirement(args);
|
|
93
|
+
}
|
|
85
94
|
const payload = this[spec.payloadBuilder](args);
|
|
86
95
|
return this.createWrapped(spec.resource, spec.wrapperKey, payload);
|
|
87
96
|
},
|
|
@@ -90,7 +99,13 @@ export const handlerMethods = {
|
|
|
90
99
|
if (spec.updateMode === 'run') {
|
|
91
100
|
return this.updateRunWithFallback(id, args);
|
|
92
101
|
}
|
|
102
|
+
if (spec.updateMode === 'requirement') {
|
|
103
|
+
return this.updateRequirement(id, args);
|
|
104
|
+
}
|
|
93
105
|
const payload = this[spec.payloadBuilder](args);
|
|
106
|
+
if (spec.updateMethod === 'patch') {
|
|
107
|
+
return this.patchWrapped(spec.resource, id, spec.wrapperKey, payload);
|
|
108
|
+
}
|
|
94
109
|
return this.updateWrapped(spec.resource, id, spec.wrapperKey, payload);
|
|
95
110
|
},
|
|
96
111
|
};
|
|
@@ -1,93 +1,151 @@
|
|
|
1
1
|
export const listingMethods = {
|
|
2
|
-
listTests({
|
|
2
|
+
listTests({
|
|
3
|
+
page,
|
|
4
|
+
per_page: perPage,
|
|
5
|
+
tql,
|
|
6
|
+
} = {}) {
|
|
3
7
|
return this.apiClient.list('tests', {
|
|
4
8
|
page,
|
|
5
9
|
per_page: perPage,
|
|
6
|
-
|
|
7
|
-
search_text: searchText || query,
|
|
8
|
-
...filters,
|
|
10
|
+
tql,
|
|
9
11
|
});
|
|
10
12
|
},
|
|
11
13
|
|
|
12
|
-
searchTests({
|
|
14
|
+
searchTests({ page, per_page: perPage, tql } = {}) {
|
|
13
15
|
return this.listTests({
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
page,
|
|
17
|
+
per_page: perPage,
|
|
18
|
+
tql,
|
|
16
19
|
});
|
|
17
20
|
},
|
|
18
21
|
|
|
19
|
-
listSuites({ page, per_page: perPage, file_type: fileType, tag, labels, search_text: searchText
|
|
22
|
+
listSuites({ page, per_page: perPage, file_type: fileType, tag, labels, search_text: searchText } = {}) {
|
|
20
23
|
return this.apiClient.list('suites', {
|
|
21
24
|
page,
|
|
22
25
|
per_page: perPage,
|
|
23
26
|
file_type: fileType,
|
|
24
27
|
tag,
|
|
25
28
|
labels,
|
|
26
|
-
search_text: searchText
|
|
29
|
+
search_text: searchText,
|
|
27
30
|
});
|
|
28
31
|
},
|
|
29
32
|
|
|
30
|
-
searchSuites({
|
|
33
|
+
searchSuites({ search_text: searchText, ...rest } = {}) {
|
|
31
34
|
return this.listSuites({
|
|
32
35
|
...rest,
|
|
33
|
-
search_text: searchText
|
|
36
|
+
search_text: searchText,
|
|
34
37
|
});
|
|
35
38
|
},
|
|
36
39
|
|
|
37
|
-
listRuns({
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return this.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
listRuns({
|
|
41
|
+
page,
|
|
42
|
+
per_page: perPage,
|
|
43
|
+
tql,
|
|
44
|
+
} = {}) {
|
|
45
|
+
return this.apiClient.list('runs', {
|
|
46
|
+
page,
|
|
47
|
+
per_page: perPage,
|
|
48
|
+
tql,
|
|
49
|
+
});
|
|
47
50
|
},
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
return this.
|
|
52
|
+
searchRuns({ page, per_page: perPage, tql } = {}) {
|
|
53
|
+
return this.listRuns({ page, per_page: perPage, tql });
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
listTestruns({
|
|
57
|
+
page,
|
|
58
|
+
per_page: perPage,
|
|
59
|
+
run_id: runId,
|
|
60
|
+
test_ids: testIds,
|
|
61
|
+
filter_status: filterStatus,
|
|
62
|
+
filter_kind: filterKind,
|
|
63
|
+
filter_user: filterUser,
|
|
64
|
+
filter_priority: filterPriority,
|
|
65
|
+
filter_substatus: filterSubstatus,
|
|
66
|
+
filter_search: filterSearch,
|
|
67
|
+
filter_message: filterMessage,
|
|
68
|
+
filter_link: filterLink,
|
|
69
|
+
filter_finished_at_date_range: filterFinishedAtDateRange,
|
|
70
|
+
tags,
|
|
71
|
+
labels,
|
|
72
|
+
envs,
|
|
73
|
+
rungroups,
|
|
74
|
+
defects,
|
|
75
|
+
} = {}) {
|
|
76
|
+
return this.apiClient.list('testruns', {
|
|
77
|
+
page,
|
|
78
|
+
per_page: perPage,
|
|
79
|
+
run_id: runId,
|
|
80
|
+
test_ids: Array.isArray(testIds) ? testIds.join(',') : testIds,
|
|
81
|
+
'filter[status]': filterStatus,
|
|
82
|
+
'filter[kind]': filterKind,
|
|
83
|
+
'filter[user]': filterUser,
|
|
84
|
+
'filter[priority]': filterPriority,
|
|
85
|
+
'filter[substatus]': filterSubstatus,
|
|
86
|
+
'filter[search]': filterSearch,
|
|
87
|
+
'filter[message]': filterMessage,
|
|
88
|
+
'filter[link]': filterLink,
|
|
89
|
+
'filter[finished_at_date_range]': filterFinishedAtDateRange,
|
|
90
|
+
tags: Array.isArray(tags) ? tags.join(',') : tags,
|
|
91
|
+
labels: Array.isArray(labels) ? labels.join(',') : labels,
|
|
92
|
+
envs: Array.isArray(envs) ? envs.join(',') : envs,
|
|
93
|
+
rungroups: Array.isArray(rungroups) ? rungroups.join(',') : rungroups,
|
|
94
|
+
defects,
|
|
95
|
+
});
|
|
51
96
|
},
|
|
52
97
|
|
|
53
|
-
|
|
54
|
-
return this.
|
|
98
|
+
searchTestruns({ page, per_page: perPage, run_id: runId, filter_search: filterSearch, ...rest } = {}) {
|
|
99
|
+
return this.listTestruns({
|
|
100
|
+
page,
|
|
101
|
+
per_page: perPage,
|
|
102
|
+
run_id: runId,
|
|
103
|
+
filter_search: filterSearch,
|
|
104
|
+
...rest,
|
|
105
|
+
});
|
|
55
106
|
},
|
|
56
107
|
|
|
57
|
-
|
|
58
|
-
return this.
|
|
108
|
+
listRungroups({ page, per_page: perPage } = {}) {
|
|
109
|
+
return this.apiClient.list('rungroups', { page, per_page: perPage });
|
|
59
110
|
},
|
|
60
111
|
|
|
61
|
-
listSteps({ page, per_page: perPage
|
|
62
|
-
return this.apiClient.list('steps', { page, per_page: perPage
|
|
112
|
+
listSteps({ page, per_page: perPage } = {}) {
|
|
113
|
+
return this.apiClient.list('steps', { page, per_page: perPage });
|
|
63
114
|
},
|
|
64
115
|
|
|
65
|
-
|
|
66
|
-
return this.
|
|
116
|
+
listSnippets({ page, per_page: perPage } = {}) {
|
|
117
|
+
return this.apiClient.list('snippets', { page, per_page: perPage });
|
|
67
118
|
},
|
|
68
119
|
|
|
69
|
-
|
|
70
|
-
return this.apiClient.list('
|
|
120
|
+
listLabels({ page, per_page: perPage } = {}) {
|
|
121
|
+
return this.apiClient.list('labels', { page, per_page: perPage });
|
|
71
122
|
},
|
|
72
123
|
|
|
73
|
-
|
|
74
|
-
return this.
|
|
124
|
+
listPlans({ page, per_page: perPage, kind, hidden, labels, search_text: searchText } = {}) {
|
|
125
|
+
return this.apiClient.list('plans', {
|
|
126
|
+
page,
|
|
127
|
+
per_page: perPage,
|
|
128
|
+
kind,
|
|
129
|
+
hidden,
|
|
130
|
+
'labels[]': labels,
|
|
131
|
+
search_text: searchText,
|
|
132
|
+
});
|
|
75
133
|
},
|
|
76
134
|
|
|
77
|
-
|
|
78
|
-
return this.
|
|
135
|
+
searchPlans({ page, per_page: perPage, search_text: searchText, ...rest } = {}) {
|
|
136
|
+
return this.listPlans({ page, per_page: perPage, search_text: searchText, ...rest });
|
|
79
137
|
},
|
|
80
138
|
|
|
81
|
-
|
|
82
|
-
return this.
|
|
139
|
+
listRequirements({ page, per_page: perPage, source, scope } = {}) {
|
|
140
|
+
return this.apiClient.list('requirements', { page, per_page: perPage, source, scope });
|
|
83
141
|
},
|
|
84
142
|
|
|
85
|
-
|
|
86
|
-
return this.
|
|
143
|
+
searchRequirements({ page, per_page: perPage, source, scope } = {}) {
|
|
144
|
+
return this.listRequirements({ page, per_page: perPage, source, scope });
|
|
87
145
|
},
|
|
88
146
|
|
|
89
|
-
|
|
90
|
-
return this.
|
|
147
|
+
listMilestones({ page, per_page: perPage, type, status } = {}) {
|
|
148
|
+
return this.apiClient.list('milestones', { page, per_page: perPage, type, status });
|
|
91
149
|
},
|
|
92
150
|
|
|
93
151
|
listTags() {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
1
4
|
export const payloadMethods = {
|
|
2
5
|
async createWrapped(resource, wrapperKey, payload) {
|
|
3
6
|
const wrappedBody = { [wrapperKey]: payload };
|
|
@@ -23,6 +26,70 @@ export const payloadMethods = {
|
|
|
23
26
|
}
|
|
24
27
|
},
|
|
25
28
|
|
|
29
|
+
async patchWrapped(resource, id, wrapperKey, payload) {
|
|
30
|
+
const wrappedBody = { [wrapperKey]: payload };
|
|
31
|
+
try {
|
|
32
|
+
return await this.apiClient.patch(resource, id, payload);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
if (!this.shouldRetryWrappedBody(error, wrapperKey)) {
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
return this.apiClient.patch(resource, id, wrappedBody);
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
async createRequirement(args = {}) {
|
|
42
|
+
const { files, ...payloadArgs } = args;
|
|
43
|
+
const payload = this.buildRequirementPayload(payloadArgs);
|
|
44
|
+
|
|
45
|
+
if (this.hasFiles(files)) {
|
|
46
|
+
return this.apiClient.createMultipart(
|
|
47
|
+
'requirements',
|
|
48
|
+
await this.buildRequirementFormData(payload, files)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return this.createWrapped('requirements', 'requirement', payload);
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
async updateRequirement(requirementId, args = {}) {
|
|
56
|
+
const { files, ...payloadArgs } = args;
|
|
57
|
+
const payload = this.buildRequirementPayload(payloadArgs);
|
|
58
|
+
|
|
59
|
+
if (this.hasFiles(files)) {
|
|
60
|
+
return this.apiClient.patchMultipart(
|
|
61
|
+
'requirements',
|
|
62
|
+
requirementId,
|
|
63
|
+
await this.buildRequirementFormData(payload, files)
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return this.patchWrapped('requirements', requirementId, 'requirement', payload);
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
hasFiles(files) {
|
|
71
|
+
return Array.isArray(files) && files.length > 0;
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
async buildRequirementFormData(payload, files = []) {
|
|
75
|
+
const formData = new FormData();
|
|
76
|
+
|
|
77
|
+
Object.entries(payload).forEach(([key, value]) => {
|
|
78
|
+
if (value === undefined || value === null) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
formData.append(key, typeof value === 'boolean' ? String(value) : value);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
for (const filePath of files) {
|
|
85
|
+
const resolvedPath = path.resolve(String(filePath));
|
|
86
|
+
const data = await fs.readFile(resolvedPath);
|
|
87
|
+
formData.append('files', new Blob([data]), path.basename(resolvedPath));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return formData;
|
|
91
|
+
},
|
|
92
|
+
|
|
26
93
|
buildTestPayload({
|
|
27
94
|
title,
|
|
28
95
|
suite_id: suiteId,
|
|
@@ -76,7 +143,7 @@ export const payloadMethods = {
|
|
|
76
143
|
buildRunCreatePayload({
|
|
77
144
|
title,
|
|
78
145
|
description,
|
|
79
|
-
|
|
146
|
+
plan_ids: planIds,
|
|
80
147
|
kind,
|
|
81
148
|
rungroup_id: rungroupId,
|
|
82
149
|
env,
|
|
@@ -85,13 +152,12 @@ export const payloadMethods = {
|
|
|
85
152
|
test_ids: testIds,
|
|
86
153
|
suite_ids: suiteIds,
|
|
87
154
|
envs,
|
|
88
|
-
configuration,
|
|
89
155
|
link,
|
|
90
156
|
} = {}) {
|
|
91
157
|
return {
|
|
92
158
|
title,
|
|
93
159
|
description,
|
|
94
|
-
|
|
160
|
+
plan_ids: planIds,
|
|
95
161
|
kind,
|
|
96
162
|
rungroup_id: rungroupId,
|
|
97
163
|
env,
|
|
@@ -100,7 +166,6 @@ export const payloadMethods = {
|
|
|
100
166
|
test_ids: testIds,
|
|
101
167
|
suite_ids: suiteIds,
|
|
102
168
|
envs,
|
|
103
|
-
configuration,
|
|
104
169
|
link,
|
|
105
170
|
};
|
|
106
171
|
},
|
|
@@ -108,7 +173,6 @@ export const payloadMethods = {
|
|
|
108
173
|
buildRunUpdatePayload({
|
|
109
174
|
title,
|
|
110
175
|
description,
|
|
111
|
-
plan_id: planId,
|
|
112
176
|
kind,
|
|
113
177
|
rungroup_id: rungroupId,
|
|
114
178
|
env,
|
|
@@ -117,13 +181,11 @@ export const payloadMethods = {
|
|
|
117
181
|
assign_strategy: assignStrategy,
|
|
118
182
|
test_ids: testIds,
|
|
119
183
|
suite_ids: suiteIds,
|
|
120
|
-
configuration,
|
|
121
184
|
link,
|
|
122
185
|
} = {}) {
|
|
123
186
|
return {
|
|
124
187
|
title,
|
|
125
188
|
description,
|
|
126
|
-
plan_id: planId,
|
|
127
189
|
kind,
|
|
128
190
|
rungroup_id: rungroupId,
|
|
129
191
|
env,
|
|
@@ -132,7 +194,6 @@ export const payloadMethods = {
|
|
|
132
194
|
assign_strategy: assignStrategy,
|
|
133
195
|
test_ids: testIds,
|
|
134
196
|
suite_ids: suiteIds,
|
|
135
|
-
configuration,
|
|
136
197
|
link,
|
|
137
198
|
};
|
|
138
199
|
},
|
|
@@ -268,4 +329,24 @@ export const payloadMethods = {
|
|
|
268
329
|
link,
|
|
269
330
|
};
|
|
270
331
|
},
|
|
332
|
+
|
|
333
|
+
buildRequirementPayload({
|
|
334
|
+
title,
|
|
335
|
+
source_type: sourceType,
|
|
336
|
+
description,
|
|
337
|
+
details,
|
|
338
|
+
active,
|
|
339
|
+
global,
|
|
340
|
+
confluence_url: confluenceUrl,
|
|
341
|
+
} = {}) {
|
|
342
|
+
return {
|
|
343
|
+
title,
|
|
344
|
+
source_type: sourceType,
|
|
345
|
+
description,
|
|
346
|
+
details,
|
|
347
|
+
active,
|
|
348
|
+
global,
|
|
349
|
+
confluence_url: confluenceUrl,
|
|
350
|
+
};
|
|
351
|
+
},
|
|
271
352
|
};
|