@aipper/zentao-mcp-server 0.1.7 → 0.1.8
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/.env.example +6 -0
- package/README.md +9 -0
- package/package.json +1 -1
- package/src/index.js +19 -0
- package/src/tools.js +33 -0
- package/src/zentao.js +121 -19
package/.env.example
CHANGED
|
@@ -11,6 +11,12 @@ ZENTAO_PASSWORD=your_password
|
|
|
11
11
|
# 可选:默认产品 ID(当实例请求 bug 列表需要 product id 时使用)
|
|
12
12
|
# ZENTAO_PRODUCT_ID=1
|
|
13
13
|
|
|
14
|
+
# 可选:我的 bug 列表路径(例如 /my/bug 或 /my/bugs)
|
|
15
|
+
# ZENTAO_MY_BUGS_PATH=/my/bug
|
|
16
|
+
|
|
17
|
+
# 可选:bug 列表回退路径(逗号分隔)
|
|
18
|
+
# ZENTAO_BUGS_FALLBACK_PATHS=/my/bug,/my/bugs
|
|
19
|
+
|
|
14
20
|
# 获取 Token 的路径(相对 ZENTAO_BASE_URL;通常为 /api.php/v1/tokens)
|
|
15
21
|
ZENTAO_TOKEN_PATH=/api.php/v1/tokens
|
|
16
22
|
|
package/README.md
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
- `ZENTAO_ACCOUNT`
|
|
18
18
|
- `ZENTAO_PASSWORD`
|
|
19
19
|
- (可选)`ZENTAO_PRODUCT_ID`:你的禅道实例若报 `Need product id` 时设置
|
|
20
|
+
- (可选)`ZENTAO_MY_BUGS_PATH`:我的 bug 专用接口路径(如 `/my/bug`)
|
|
21
|
+
- (可选)`ZENTAO_BUGS_FALLBACK_PATHS`:bug 列表回退路径(逗号分隔)
|
|
20
22
|
|
|
21
23
|
> 注意:不同禅道版本/部署方式的 token 端点与返回结构可能不同;可通过 `ZENTAO_TOKEN_PATH`/`ZENTAO_API_PREFIX` 调整。
|
|
22
24
|
>
|
|
@@ -88,6 +90,7 @@ npm run smoke
|
|
|
88
90
|
`-32000` 通常是客户端侧“通用 MCP 调用失败”映射码,优先检查:
|
|
89
91
|
- `env` 是否完整传入(尤其是 `ZENTAO_BASE_URL`/`ZENTAO_ACCOUNT`/`ZENTAO_PASSWORD`)。
|
|
90
92
|
- 若报 `Need product id`,请设置 `ZENTAO_PRODUCT_ID`,或在 `get_my_bugs` 传 `productId`。
|
|
93
|
+
- 若你的 bug 在“项目集/我的视角”而非产品,建议设置 `ZENTAO_MY_BUGS_PATH=/my/bug`(或 `/my/bugs`)。
|
|
91
94
|
- `ZENTAO_API_PREFIX`/`ZENTAO_TOKEN_PATH` 是否和你的禅道实例一致。
|
|
92
95
|
- MCP 客户端是否真的在执行 `npx -y @aipper/zentao-mcp-server`(而不是旧的本地命令)。
|
|
93
96
|
- 客户端日志中是否有启动报错(如找不到命令、401、超时)。
|
|
@@ -99,16 +102,22 @@ npm run smoke
|
|
|
99
102
|
- `get_my_bugs`:获取“指派给我”的 bug(支持 `status`/`keyword`/`limit`/`page`/`productId`,默认路径 `/bugs`)
|
|
100
103
|
- `get_bug_detail`:按 `id` 获取 bug 详情(默认路径模板 `/bugs/{id}`,返回详情与图片链接)
|
|
101
104
|
- `resolve_bug`:按 `id` 处理单个 bug 状态(默认 `resolution=fixed`)
|
|
105
|
+
- `resolve_bug`:按 `id` 处理单个 bug 状态(默认 `resolution=fixed`,支持 `solution` 解决说明)
|
|
102
106
|
- `batch_resolve_my_bugs`:批量处理“我的 bug”(默认筛选 `status=active`,支持 `productId`)
|
|
103
107
|
- `close_bug`:按 `id` 关闭 bug
|
|
104
108
|
- `verify_bug`:验证结果处理(`pass`=关闭,`fail`=激活)
|
|
109
|
+
- `comment_bug`:按 `id` 添加备注(默认路径 `/bugs/{id}/comment`)
|
|
105
110
|
|
|
106
111
|
示例参数:
|
|
107
112
|
- `resolve_bug`:`{"id":123,"resolution":"fixed","comment":"已修复并自测"}`
|
|
113
|
+
- `resolve_bug`(建议):`{"id":123,"resolution":"fixed","solution":"修复空指针并补充参数校验"}`
|
|
108
114
|
- `batch_resolve_my_bugs`:`{"status":"active","maxItems":20,"comment":"批量修复"}`
|
|
115
|
+
- `batch_resolve_my_bugs`(建议):`{"status":"active","maxItems":20,"solution":"统一修复分页参数为空导致的报错"}`
|
|
109
116
|
- `get_my_bugs`(按产品):`{"status":"active","productId":1,"limit":50}`
|
|
117
|
+
- `get_my_bugs`(项目集/我的):`{"status":"active","path":"/my/bug","limit":50}`
|
|
110
118
|
- `close_bug`:`{"id":123,"comment":"验证通过,关闭"}`
|
|
111
119
|
- `verify_bug`:`{"id":123,"result":"pass","comment":"验证通过"}`
|
|
120
|
+
- `comment_bug`:`{"id":123,"comment":"已复现,正在定位根因"}`
|
|
112
121
|
|
|
113
122
|
## 安全建议
|
|
114
123
|
- 使用最小权限账号(仅需要的项目权限),避免使用管理员账号。
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -18,6 +18,7 @@ const KNOWN_TOOL_NAMES = new Set([
|
|
|
18
18
|
"batch_resolve_my_bugs",
|
|
19
19
|
"close_bug",
|
|
20
20
|
"verify_bug",
|
|
21
|
+
"comment_bug",
|
|
21
22
|
]);
|
|
22
23
|
|
|
23
24
|
function normalizeToolName(rawName) {
|
|
@@ -49,6 +50,11 @@ function getConfigFromEnv() {
|
|
|
49
50
|
const timeoutMs = Number(process.env.ZENTAO_HTTP_TIMEOUT_MS || "30000");
|
|
50
51
|
const exposeToken = String(process.env.ZENTAO_EXPOSE_TOKEN || "false").toLowerCase() === "true";
|
|
51
52
|
const defaultProductId = Number(process.env.ZENTAO_PRODUCT_ID || "0") || null;
|
|
53
|
+
const myBugsPath = String(process.env.ZENTAO_MY_BUGS_PATH || "").trim();
|
|
54
|
+
const bugsFallbackPaths = String(process.env.ZENTAO_BUGS_FALLBACK_PATHS || "")
|
|
55
|
+
.split(",")
|
|
56
|
+
.map((value) => value.trim())
|
|
57
|
+
.filter(Boolean);
|
|
52
58
|
|
|
53
59
|
const account = requireEnv("ZENTAO_ACCOUNT");
|
|
54
60
|
const password = requireEnv("ZENTAO_PASSWORD");
|
|
@@ -61,6 +67,8 @@ function getConfigFromEnv() {
|
|
|
61
67
|
timeoutMs,
|
|
62
68
|
exposeToken,
|
|
63
69
|
defaultProductId,
|
|
70
|
+
myBugsPath,
|
|
71
|
+
bugsFallbackPaths,
|
|
64
72
|
auth: { account, password },
|
|
65
73
|
};
|
|
66
74
|
}
|
|
@@ -134,6 +142,7 @@ async function main() {
|
|
|
134
142
|
const resp = await zentao.resolveBug({
|
|
135
143
|
id: args.id,
|
|
136
144
|
resolution: args.resolution || "fixed",
|
|
145
|
+
solution: args.solution || "",
|
|
137
146
|
comment: args.comment || "",
|
|
138
147
|
path: args.path || "/bugs/{id}/resolve",
|
|
139
148
|
});
|
|
@@ -150,6 +159,7 @@ async function main() {
|
|
|
150
159
|
maxItems: args.maxItems,
|
|
151
160
|
assignedTo: args.assignedTo || "",
|
|
152
161
|
resolution: args.resolution || "fixed",
|
|
162
|
+
solution: args.solution || "",
|
|
153
163
|
comment: args.comment || "",
|
|
154
164
|
listPath: args.listPath || "/bugs",
|
|
155
165
|
resolvePath: args.resolvePath || "/bugs/{id}/resolve",
|
|
@@ -178,6 +188,15 @@ async function main() {
|
|
|
178
188
|
return toMcpTextResult(JSON.stringify(resp, null, 2));
|
|
179
189
|
}
|
|
180
190
|
|
|
191
|
+
if (toolName === "comment_bug") {
|
|
192
|
+
const resp = await zentao.commentBug({
|
|
193
|
+
id: args.id,
|
|
194
|
+
comment: args.comment || "",
|
|
195
|
+
path: args.path || "/bugs/{id}/comment",
|
|
196
|
+
});
|
|
197
|
+
return toMcpTextResult(JSON.stringify(resp, null, 2));
|
|
198
|
+
}
|
|
199
|
+
|
|
181
200
|
throw new Error(`Unknown tool: ${rawToolName}`);
|
|
182
201
|
} catch (err) {
|
|
183
202
|
const errorPayload = {
|
package/src/tools.js
CHANGED
|
@@ -80,6 +80,7 @@ export const TOOLS = [
|
|
|
80
80
|
properties: {
|
|
81
81
|
id: { type: "number", minimum: 1, description: "Bug ID" },
|
|
82
82
|
resolution: { type: "string", description: "Default fixed" },
|
|
83
|
+
solution: { type: "string", description: "Resolution description (preferred)" },
|
|
83
84
|
comment: { type: "string", description: "Optional resolve comment" },
|
|
84
85
|
path: { type: "string", description: "Optional resolve endpoint template, default /bugs/{id}/resolve" },
|
|
85
86
|
},
|
|
@@ -101,6 +102,7 @@ export const TOOLS = [
|
|
|
101
102
|
maxItems: { type: "number", minimum: 1, maximum: 500, description: "Max resolve count, default 50" },
|
|
102
103
|
assignedTo: { type: "string", description: "Optional assignee override" },
|
|
103
104
|
resolution: { type: "string", description: "Default fixed" },
|
|
105
|
+
solution: { type: "string", description: "Resolution description (preferred)" },
|
|
104
106
|
comment: { type: "string", description: "Optional resolve comment" },
|
|
105
107
|
listPath: { type: "string", description: "Optional list endpoint, default /bugs" },
|
|
106
108
|
resolvePath: { type: "string", description: "Optional resolve path template, default /bugs/{id}/resolve" },
|
|
@@ -139,6 +141,20 @@ export const TOOLS = [
|
|
|
139
141
|
additionalProperties: false,
|
|
140
142
|
},
|
|
141
143
|
},
|
|
144
|
+
{
|
|
145
|
+
name: "comment_bug",
|
|
146
|
+
description: "Add comment to one bug by ID.",
|
|
147
|
+
inputSchema: {
|
|
148
|
+
type: "object",
|
|
149
|
+
properties: {
|
|
150
|
+
id: { type: "number", minimum: 1, description: "Bug ID" },
|
|
151
|
+
comment: { type: "string", description: "Comment content" },
|
|
152
|
+
path: { type: "string", description: "Optional comment endpoint template, default /bugs/{id}/comment" },
|
|
153
|
+
},
|
|
154
|
+
required: ["id", "comment"],
|
|
155
|
+
additionalProperties: false,
|
|
156
|
+
},
|
|
157
|
+
},
|
|
142
158
|
];
|
|
143
159
|
|
|
144
160
|
export function assertToolArgs(name, args) {
|
|
@@ -176,6 +192,9 @@ export function assertToolArgs(name, args) {
|
|
|
176
192
|
if (args.path !== undefined && typeof args.path !== "string") {
|
|
177
193
|
throw new Error("resolve_bug.path must be a string");
|
|
178
194
|
}
|
|
195
|
+
if (args.solution !== undefined && typeof args.solution !== "string") {
|
|
196
|
+
throw new Error("resolve_bug.solution must be a string");
|
|
197
|
+
}
|
|
179
198
|
}
|
|
180
199
|
if (name === "batch_resolve_my_bugs") {
|
|
181
200
|
if (args.limit !== undefined && (!Number.isFinite(args.limit) || args.limit < 1 || args.limit > 200)) {
|
|
@@ -196,6 +215,9 @@ export function assertToolArgs(name, args) {
|
|
|
196
215
|
if (args.resolvePath !== undefined && typeof args.resolvePath !== "string") {
|
|
197
216
|
throw new Error("batch_resolve_my_bugs.resolvePath must be a string");
|
|
198
217
|
}
|
|
218
|
+
if (args.solution !== undefined && typeof args.solution !== "string") {
|
|
219
|
+
throw new Error("batch_resolve_my_bugs.solution must be a string");
|
|
220
|
+
}
|
|
199
221
|
}
|
|
200
222
|
if (name === "close_bug") {
|
|
201
223
|
if (!Number.isFinite(args.id) || Number(args.id) < 1) {
|
|
@@ -222,4 +244,15 @@ export function assertToolArgs(name, args) {
|
|
|
222
244
|
throw new Error("verify_bug.activatePath must be a string");
|
|
223
245
|
}
|
|
224
246
|
}
|
|
247
|
+
if (name === "comment_bug") {
|
|
248
|
+
if (!Number.isFinite(args.id) || Number(args.id) < 1) {
|
|
249
|
+
throw new Error("comment_bug.id must be a number >= 1");
|
|
250
|
+
}
|
|
251
|
+
if (typeof args.comment !== "string" || !args.comment.trim()) {
|
|
252
|
+
throw new Error("comment_bug.comment must be a non-empty string");
|
|
253
|
+
}
|
|
254
|
+
if (args.path !== undefined && typeof args.path !== "string") {
|
|
255
|
+
throw new Error("comment_bug.path must be a string");
|
|
256
|
+
}
|
|
257
|
+
}
|
|
225
258
|
}
|
package/src/zentao.js
CHANGED
|
@@ -31,7 +31,17 @@ function createAbortSignal(timeoutMs) {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export function createZenTaoClient(config) {
|
|
34
|
-
const {
|
|
34
|
+
const {
|
|
35
|
+
baseUrl,
|
|
36
|
+
apiPrefix,
|
|
37
|
+
tokenPath,
|
|
38
|
+
tokenTtlMs,
|
|
39
|
+
timeoutMs,
|
|
40
|
+
defaultProductId,
|
|
41
|
+
myBugsPath,
|
|
42
|
+
bugsFallbackPaths,
|
|
43
|
+
auth,
|
|
44
|
+
} = config;
|
|
35
45
|
|
|
36
46
|
let cachedToken = "";
|
|
37
47
|
let cachedAt = 0;
|
|
@@ -193,11 +203,25 @@ export function createZenTaoClient(config) {
|
|
|
193
203
|
return basePath;
|
|
194
204
|
}
|
|
195
205
|
|
|
206
|
+
function buildMyBugsPath(pathTemplate) {
|
|
207
|
+
const template = String(pathTemplate || "").trim();
|
|
208
|
+
if (!template) return "";
|
|
209
|
+
return template.replaceAll("{account}", encodeURIComponent(auth.account || ""));
|
|
210
|
+
}
|
|
211
|
+
|
|
196
212
|
function isNeedProductIdError(err) {
|
|
197
213
|
const merged = `${String(err?.message || "")} ${JSON.stringify(err?.data || "")}`.toLowerCase();
|
|
198
214
|
return merged.includes("need product id");
|
|
199
215
|
}
|
|
200
216
|
|
|
217
|
+
function buildResolutionComment({ solution, comment, resolution }) {
|
|
218
|
+
const normalizedSolution = String(solution || "").trim();
|
|
219
|
+
if (normalizedSolution) return `解决说明:${normalizedSolution}`;
|
|
220
|
+
const normalizedComment = String(comment || "").trim();
|
|
221
|
+
if (normalizedComment) return normalizedComment;
|
|
222
|
+
return `已处理,resolution=${String(resolution || "fixed")}`;
|
|
223
|
+
}
|
|
224
|
+
|
|
201
225
|
function getBugAssignee(bug) {
|
|
202
226
|
return (
|
|
203
227
|
bug?.assignedTo ||
|
|
@@ -256,6 +280,16 @@ export function createZenTaoClient(config) {
|
|
|
256
280
|
return buildBugTransitionPath({ id, path, action: "resolve" });
|
|
257
281
|
}
|
|
258
282
|
|
|
283
|
+
function buildBugCommentPath({ id, path }) {
|
|
284
|
+
const normalizedId = Number(id);
|
|
285
|
+
const basePath = path || "/bugs/{id}/comment";
|
|
286
|
+
if (basePath.includes("{id}")) {
|
|
287
|
+
return basePath.replaceAll("{id}", String(normalizedId));
|
|
288
|
+
}
|
|
289
|
+
const trimmed = basePath.replace(/\/+$/, "");
|
|
290
|
+
return `${trimmed}/${normalizedId}/comment`;
|
|
291
|
+
}
|
|
292
|
+
|
|
259
293
|
function buildBugTransitionPath({ id, path, action }) {
|
|
260
294
|
const normalizedId = Number(id);
|
|
261
295
|
const safeAction = String(action || "").trim();
|
|
@@ -313,6 +347,11 @@ export function createZenTaoClient(config) {
|
|
|
313
347
|
const assignee = normalizeString(assignedTo) || normalizeString(auth.account);
|
|
314
348
|
const effectiveProductId = normalizePositiveInt(productId) || normalizePositiveInt(defaultProductId);
|
|
315
349
|
const primaryPath = buildProductScopedBugsPath({ productId: effectiveProductId, path });
|
|
350
|
+
const configuredMyBugsPath = buildMyBugsPath(myBugsPath);
|
|
351
|
+
const fallbackPathCandidates = (bugsFallbackPaths && bugsFallbackPaths.length > 0)
|
|
352
|
+
? bugsFallbackPaths
|
|
353
|
+
: ["/my/bug", "/my/bugs"];
|
|
354
|
+
const fallbackPaths = fallbackPathCandidates.map((item) => buildMyBugsPath(item)).filter(Boolean);
|
|
316
355
|
|
|
317
356
|
const query = {
|
|
318
357
|
limit: safeLimit,
|
|
@@ -321,21 +360,34 @@ export function createZenTaoClient(config) {
|
|
|
321
360
|
status: status || undefined,
|
|
322
361
|
product: effectiveProductId || undefined,
|
|
323
362
|
};
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
363
|
+
const candidatePaths = [];
|
|
364
|
+
candidatePaths.push(primaryPath);
|
|
365
|
+
if (effectiveProductId) {
|
|
366
|
+
const productPath = `/products/${effectiveProductId}/bugs`;
|
|
367
|
+
if (!candidatePaths.includes(productPath)) candidatePaths.push(productPath);
|
|
368
|
+
}
|
|
369
|
+
if (configuredMyBugsPath && !candidatePaths.includes(configuredMyBugsPath)) {
|
|
370
|
+
candidatePaths.push(configuredMyBugsPath);
|
|
371
|
+
}
|
|
372
|
+
for (const fallback of fallbackPaths) {
|
|
373
|
+
if (!candidatePaths.includes(fallback)) candidatePaths.push(fallback);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
let resp = null;
|
|
377
|
+
let usedPath = primaryPath;
|
|
378
|
+
let lastErr = null;
|
|
379
|
+
for (const candidate of candidatePaths) {
|
|
380
|
+
try {
|
|
381
|
+
usedPath = candidate;
|
|
382
|
+
resp = await call({ path: candidate, method: "GET", query });
|
|
383
|
+
break;
|
|
384
|
+
} catch (err) {
|
|
385
|
+
lastErr = err;
|
|
386
|
+
if (!isNeedProductIdError(err)) continue;
|
|
387
|
+
if (candidate !== primaryPath) continue;
|
|
337
388
|
}
|
|
338
389
|
}
|
|
390
|
+
if (!resp) throw lastErr;
|
|
339
391
|
|
|
340
392
|
const bugs = parseBugsFromResponse(resp?.data);
|
|
341
393
|
const filtered = bugs.filter((bug) => matchesBugFilters(bug, { status, keyword, assignee }));
|
|
@@ -348,7 +400,7 @@ export function createZenTaoClient(config) {
|
|
|
348
400
|
productId: effectiveProductId,
|
|
349
401
|
assignedTo: assignee,
|
|
350
402
|
bugs: filtered,
|
|
351
|
-
raw: { status: resp?.status, path:
|
|
403
|
+
raw: { status: resp?.status, path: usedPath },
|
|
352
404
|
};
|
|
353
405
|
}
|
|
354
406
|
|
|
@@ -379,21 +431,33 @@ export function createZenTaoClient(config) {
|
|
|
379
431
|
};
|
|
380
432
|
}
|
|
381
433
|
|
|
382
|
-
async function resolveBug({
|
|
434
|
+
async function resolveBug({
|
|
435
|
+
id,
|
|
436
|
+
resolution = "fixed",
|
|
437
|
+
solution = "",
|
|
438
|
+
comment = "",
|
|
439
|
+
path = "/bugs/{id}/resolve",
|
|
440
|
+
} = {}) {
|
|
383
441
|
const bugId = Number(id);
|
|
384
442
|
if (!Number.isFinite(bugId) || bugId < 1) {
|
|
385
443
|
throw new Error("resolveBug requires a valid bug id");
|
|
386
444
|
}
|
|
387
445
|
|
|
388
446
|
const resolvePath = buildBugResolvePath({ id: bugId, path });
|
|
389
|
-
const
|
|
390
|
-
|
|
447
|
+
const resolvedValue = String(resolution || "fixed");
|
|
448
|
+
const resolvedComment = buildResolutionComment({ solution, comment, resolution: resolvedValue });
|
|
449
|
+
const body = {
|
|
450
|
+
resolution: resolvedValue,
|
|
451
|
+
comment: resolvedComment,
|
|
452
|
+
};
|
|
391
453
|
|
|
392
454
|
const resp = await call({ path: resolvePath, method: "POST", body });
|
|
393
455
|
return {
|
|
394
456
|
id: bugId,
|
|
395
457
|
resolved: true,
|
|
396
|
-
resolution:
|
|
458
|
+
resolution: resolvedValue,
|
|
459
|
+
solution: String(solution || "").trim(),
|
|
460
|
+
comment: resolvedComment,
|
|
397
461
|
raw: { status: resp.status, data: resp.data },
|
|
398
462
|
};
|
|
399
463
|
}
|
|
@@ -467,6 +531,41 @@ export function createZenTaoClient(config) {
|
|
|
467
531
|
};
|
|
468
532
|
}
|
|
469
533
|
|
|
534
|
+
async function commentBug({ id, comment, path = "/bugs/{id}/comment" } = {}) {
|
|
535
|
+
const bugId = Number(id);
|
|
536
|
+
if (!Number.isFinite(bugId) || bugId < 1) {
|
|
537
|
+
throw new Error("commentBug requires a valid bug id");
|
|
538
|
+
}
|
|
539
|
+
const text = String(comment || "").trim();
|
|
540
|
+
if (!text) {
|
|
541
|
+
throw new Error("commentBug requires non-empty comment");
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const primaryPath = buildBugCommentPath({ id: bugId, path });
|
|
545
|
+
const body = { comment: text };
|
|
546
|
+
try {
|
|
547
|
+
const resp = await call({ path: primaryPath, method: "POST", body });
|
|
548
|
+
return {
|
|
549
|
+
id: bugId,
|
|
550
|
+
commented: true,
|
|
551
|
+
comment: text,
|
|
552
|
+
raw: { status: resp.status, path: primaryPath, data: resp.data },
|
|
553
|
+
};
|
|
554
|
+
} catch (err) {
|
|
555
|
+
const fallbackPath = primaryPath.replace(/\/comment$/, "/comments");
|
|
556
|
+
if (fallbackPath !== primaryPath && Number(err?.status) === 404) {
|
|
557
|
+
const resp = await call({ path: fallbackPath, method: "POST", body });
|
|
558
|
+
return {
|
|
559
|
+
id: bugId,
|
|
560
|
+
commented: true,
|
|
561
|
+
comment: text,
|
|
562
|
+
raw: { status: resp.status, path: fallbackPath, data: resp.data },
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
throw err;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
470
569
|
async function batchResolveMyBugs({
|
|
471
570
|
status = "active",
|
|
472
571
|
keyword = "",
|
|
@@ -476,6 +575,7 @@ export function createZenTaoClient(config) {
|
|
|
476
575
|
maxItems = 50,
|
|
477
576
|
assignedTo = "",
|
|
478
577
|
resolution = "fixed",
|
|
578
|
+
solution = "",
|
|
479
579
|
comment = "",
|
|
480
580
|
listPath = "/bugs",
|
|
481
581
|
resolvePath = "/bugs/{id}/resolve",
|
|
@@ -507,6 +607,7 @@ export function createZenTaoClient(config) {
|
|
|
507
607
|
const result = await resolveBug({
|
|
508
608
|
id: bugId,
|
|
509
609
|
resolution,
|
|
610
|
+
solution,
|
|
510
611
|
comment,
|
|
511
612
|
path: resolvePath,
|
|
512
613
|
});
|
|
@@ -552,6 +653,7 @@ export function createZenTaoClient(config) {
|
|
|
552
653
|
resolveBug,
|
|
553
654
|
closeBug,
|
|
554
655
|
verifyBug,
|
|
656
|
+
commentBug,
|
|
555
657
|
batchResolveMyBugs,
|
|
556
658
|
};
|
|
557
659
|
}
|