@cloudglab/zentao-cli 0.1.3
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/CHANGELOG.md +50 -0
- package/MCP_ZENTAO_REBUILD_PLAN.md +505 -0
- package/README.md +319 -0
- package/dist/api/bug.d.ts +32 -0
- package/dist/api/bug.js +85 -0
- package/dist/api/bug.js.map +1 -0
- package/dist/api/build.d.ts +31 -0
- package/dist/api/build.js +21 -0
- package/dist/api/build.js.map +1 -0
- package/dist/api/comment.d.ts +20 -0
- package/dist/api/comment.js +47 -0
- package/dist/api/comment.js.map +1 -0
- package/dist/api/development-context.d.ts +14 -0
- package/dist/api/development-context.js +35 -0
- package/dist/api/development-context.js.map +1 -0
- package/dist/api/execution.d.ts +46 -0
- package/dist/api/execution.js +47 -0
- package/dist/api/execution.js.map +1 -0
- package/dist/api/index.d.ts +43 -0
- package/dist/api/index.js +46 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/plan.d.ts +18 -0
- package/dist/api/plan.js +33 -0
- package/dist/api/plan.js.map +1 -0
- package/dist/api/product.d.ts +7 -0
- package/dist/api/product.js +14 -0
- package/dist/api/product.js.map +1 -0
- package/dist/api/program.d.ts +7 -0
- package/dist/api/program.js +14 -0
- package/dist/api/program.js.map +1 -0
- package/dist/api/project.d.ts +8 -0
- package/dist/api/project.js +15 -0
- package/dist/api/project.js.map +1 -0
- package/dist/api/relation.d.ts +17 -0
- package/dist/api/relation.js +66 -0
- package/dist/api/relation.js.map +1 -0
- package/dist/api/release.d.ts +6 -0
- package/dist/api/release.js +11 -0
- package/dist/api/release.js.map +1 -0
- package/dist/api/search.d.ts +17 -0
- package/dist/api/search.js +168 -0
- package/dist/api/search.js.map +1 -0
- package/dist/api/statistics.d.ts +13 -0
- package/dist/api/statistics.js +67 -0
- package/dist/api/statistics.js.map +1 -0
- package/dist/api/story.d.ts +14 -0
- package/dist/api/story.js +23 -0
- package/dist/api/story.js.map +1 -0
- package/dist/api/task.d.ts +17 -0
- package/dist/api/task.js +37 -0
- package/dist/api/task.js.map +1 -0
- package/dist/api/testcase.d.ts +51 -0
- package/dist/api/testcase.js +31 -0
- package/dist/api/testcase.js.map +1 -0
- package/dist/api/testtask.d.ts +41 -0
- package/dist/api/testtask.js +34 -0
- package/dist/api/testtask.js.map +1 -0
- package/dist/api/user.d.ts +6 -0
- package/dist/api/user.js +9 -0
- package/dist/api/user.js.map +1 -0
- package/dist/bin/zentao-dev.d.ts +2 -0
- package/dist/bin/zentao-dev.js +8 -0
- package/dist/bin/zentao-dev.js.map +1 -0
- package/dist/bin/zentao-pm.d.ts +2 -0
- package/dist/bin/zentao-pm.js +8 -0
- package/dist/bin/zentao-pm.js.map +1 -0
- package/dist/bin/zentao-qa.d.ts +2 -0
- package/dist/bin/zentao-qa.js +8 -0
- package/dist/bin/zentao-qa.js.map +1 -0
- package/dist/bin/zentao.d.ts +2 -0
- package/dist/bin/zentao.js +8 -0
- package/dist/bin/zentao.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +74 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/api-provider.d.ts +3 -0
- package/dist/core/api-provider.js +17 -0
- package/dist/core/api-provider.js.map +1 -0
- package/dist/core/auth.d.ts +13 -0
- package/dist/core/auth.js +90 -0
- package/dist/core/auth.js.map +1 -0
- package/dist/core/cli-registry.d.ts +18 -0
- package/dist/core/cli-registry.js +128 -0
- package/dist/core/cli-registry.js.map +1 -0
- package/dist/core/config.d.ts +7 -0
- package/dist/core/config.js +58 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/http.d.ts +12 -0
- package/dist/core/http.js +50 -0
- package/dist/core/http.js.map +1 -0
- package/dist/core/list-result.d.ts +14 -0
- package/dist/core/list-result.js +59 -0
- package/dist/core/list-result.js.map +1 -0
- package/dist/core/pagination.d.ts +9 -0
- package/dist/core/pagination.js +12 -0
- package/dist/core/pagination.js.map +1 -0
- package/dist/core/roles.d.ts +4 -0
- package/dist/core/roles.js +13 -0
- package/dist/core/roles.js.map +1 -0
- package/dist/core/tool-registry.d.ts +3 -0
- package/dist/core/tool-registry.js +77 -0
- package/dist/core/tool-registry.js.map +1 -0
- package/dist/core/write-guard.d.ts +25 -0
- package/dist/core/write-guard.js +53 -0
- package/dist/core/write-guard.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/bug.d.ts +2 -0
- package/dist/tools/bug.js +41 -0
- package/dist/tools/bug.js.map +1 -0
- package/dist/tools/build.d.ts +2 -0
- package/dist/tools/build.js +8 -0
- package/dist/tools/build.js.map +1 -0
- package/dist/tools/comment.d.ts +2 -0
- package/dist/tools/comment.js +23 -0
- package/dist/tools/comment.js.map +1 -0
- package/dist/tools/context.d.ts +2 -0
- package/dist/tools/context.js +11 -0
- package/dist/tools/context.js.map +1 -0
- package/dist/tools/execution.d.ts +2 -0
- package/dist/tools/execution.js +21 -0
- package/dist/tools/execution.js.map +1 -0
- package/dist/tools/init.d.ts +2 -0
- package/dist/tools/init.js +28 -0
- package/dist/tools/init.js.map +1 -0
- package/dist/tools/phase3a.d.ts +4 -0
- package/dist/tools/phase3a.js +123 -0
- package/dist/tools/phase3a.js.map +1 -0
- package/dist/tools/phase3b.d.ts +5 -0
- package/dist/tools/phase3b.js +161 -0
- package/dist/tools/phase3b.js.map +1 -0
- package/dist/tools/plan.d.ts +2 -0
- package/dist/tools/plan.js +14 -0
- package/dist/tools/plan.js.map +1 -0
- package/dist/tools/product.d.ts +2 -0
- package/dist/tools/product.js +10 -0
- package/dist/tools/product.js.map +1 -0
- package/dist/tools/profile.d.ts +2 -0
- package/dist/tools/profile.js +6 -0
- package/dist/tools/profile.js.map +1 -0
- package/dist/tools/program.d.ts +2 -0
- package/dist/tools/program.js +8 -0
- package/dist/tools/program.js.map +1 -0
- package/dist/tools/project.d.ts +2 -0
- package/dist/tools/project.js +13 -0
- package/dist/tools/project.js.map +1 -0
- package/dist/tools/relation.d.ts +2 -0
- package/dist/tools/relation.js +13 -0
- package/dist/tools/relation.js.map +1 -0
- package/dist/tools/release.d.ts +2 -0
- package/dist/tools/release.js +7 -0
- package/dist/tools/release.js.map +1 -0
- package/dist/tools/search.d.ts +2 -0
- package/dist/tools/search.js +18 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/shared.d.ts +2 -0
- package/dist/tools/shared.js +11 -0
- package/dist/tools/shared.js.map +1 -0
- package/dist/tools/statistics.d.ts +2 -0
- package/dist/tools/statistics.js +10 -0
- package/dist/tools/statistics.js.map +1 -0
- package/dist/tools/story.d.ts +2 -0
- package/dist/tools/story.js +14 -0
- package/dist/tools/story.js.map +1 -0
- package/dist/tools/task.d.ts +2 -0
- package/dist/tools/task.js +48 -0
- package/dist/tools/task.js.map +1 -0
- package/dist/tools/testcase.d.ts +2 -0
- package/dist/tools/testcase.js +16 -0
- package/dist/tools/testcase.js.map +1 -0
- package/dist/tools/testtask.d.ts +2 -0
- package/dist/tools/testtask.js +14 -0
- package/dist/tools/testtask.js.map +1 -0
- package/dist/types/common.d.ts +15 -0
- package/dist/types/common.js +2 -0
- package/dist/types/common.js.map +1 -0
- package/dist/types/zentao.d.ts +42 -0
- package/dist/types/zentao.js +2 -0
- package/dist/types/zentao.js.map +1 -0
- package/dist/utils/json.d.ts +2 -0
- package/dist/utils/json.js +27 -0
- package/dist/utils/json.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# @cloudglab/zentao-cli
|
|
2
|
+
|
|
3
|
+
基于 TypeScript 重建的禅道命令行工具,优先适配当前禅道 `18.5` 的 REST v1 API。
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
> 把任务、Bug、需求、测试、计划、构建、发布这些禅道操作,直接接进本地 `zentao` 命令,给命令行、脚本和 skill 调用。
|
|
8
|
+
|
|
9
|
+
## 亮点
|
|
10
|
+
|
|
11
|
+
- `60` 个 CLI 命令,覆盖任务、Bug、需求、执行、测试、计划、构建、发布、搜索等核心场景
|
|
12
|
+
- `4` 个内置角色:`Dev / QA / PM / Full`
|
|
13
|
+
- 基于 TypeScript 重建,优先适配禅道 `18.5` REST v1 API
|
|
14
|
+
- 发布产物运行时目标:`Node.js >= 16`
|
|
15
|
+
- 默认写保护,写操作需要显式开启并确认,更适合接入智能体和自动化流程
|
|
16
|
+
|
|
17
|
+
## 快速开始
|
|
18
|
+
|
|
19
|
+
环境要求:`Node.js >= 16`
|
|
20
|
+
|
|
21
|
+
### 安装
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# 运行时要求:Node.js >= 16
|
|
25
|
+
|
|
26
|
+
# 直接运行
|
|
27
|
+
npx -y @cloudglab/zentao-cli@latest
|
|
28
|
+
|
|
29
|
+
# 全局安装
|
|
30
|
+
npm i -g @cloudglab/zentao-cli@latest
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 配置
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
export ZENTAO_URL="https://your-zentao.example.com"
|
|
37
|
+
export ZENTAO_USERNAME="your-account"
|
|
38
|
+
export ZENTAO_PASSWORD="your-password"
|
|
39
|
+
export ZENTAO_API_VERSION="v1"
|
|
40
|
+
|
|
41
|
+
# 可选:非标准部署时指定完整 API 基础地址
|
|
42
|
+
# export ZENTAO_API_BASE_URL="https://your-zentao.example.com/custom/api.php/v1"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 运行
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# 查看帮助 / 版本
|
|
49
|
+
zentao help
|
|
50
|
+
zentao --version
|
|
51
|
+
|
|
52
|
+
# 默认 full 角色
|
|
53
|
+
zentao list
|
|
54
|
+
zentao getMyTasks --status all --limit 20
|
|
55
|
+
|
|
56
|
+
# 按角色启动
|
|
57
|
+
zentao --role dev getMyTasks --status all
|
|
58
|
+
zentao --role qa getMyBugs --limit 50
|
|
59
|
+
zentao --role pm getProducts
|
|
60
|
+
|
|
61
|
+
# 也可以直接用 npx
|
|
62
|
+
npx -y @cloudglab/zentao-cli@latest --role qa getMyBugs --limit 50
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 使用方式
|
|
66
|
+
|
|
67
|
+
### CLI
|
|
68
|
+
|
|
69
|
+
适合手动查数据、排查问题、批量执行脚本。
|
|
70
|
+
|
|
71
|
+
常见场景:
|
|
72
|
+
- 查我的任务 / Bug / 统计
|
|
73
|
+
- 查询需求、执行、计划、构建、发布
|
|
74
|
+
- 创建或更新任务、测试用例、测试单
|
|
75
|
+
- 做任务流转、Bug 处理、批量排期、回填工时、同步状态
|
|
76
|
+
- 通过 `Dev / QA / PM / Full` 角色入口提供差异化能力
|
|
77
|
+
|
|
78
|
+
### 智能体 / Skill 接入建议
|
|
79
|
+
|
|
80
|
+
推荐顺序:
|
|
81
|
+
|
|
82
|
+
1. 先检查本机有没有 `zentao`
|
|
83
|
+
2. 没有就安装:`npm i -g @cloudglab/zentao-cli@latest`
|
|
84
|
+
3. skill 内优先调用本地 `zentao`
|
|
85
|
+
4. 只在不能安装时,才退回 `npx -y @cloudglab/zentao-cli@latest`
|
|
86
|
+
|
|
87
|
+
常用检查命令:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
command -v zentao
|
|
91
|
+
zentao --version
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
skill / 自动化里推荐直接执行:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
zentao --role qa getMyBugs --limit 50
|
|
98
|
+
zentao getProductBugs --productId 123 --status all --limit 100
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Skill
|
|
102
|
+
|
|
103
|
+
适合把当前能力包装成可复用的 AI Skill,对外推广更友好。
|
|
104
|
+
|
|
105
|
+
相关文档:
|
|
106
|
+
- `skills/zentao-cli/reference/overview.md`
|
|
107
|
+
- `skills/zentao-cli/reference/install.md`
|
|
108
|
+
|
|
109
|
+
安装 skill 时,统一走:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npx skills add @cloudglab/zentao-cli -g
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
skill 内部入口建议:
|
|
116
|
+
|
|
117
|
+
- 优先:`zentao`
|
|
118
|
+
- 或按角色:`zentao --role qa`
|
|
119
|
+
- 无本地安装时兜底:`npx -y @cloudglab/zentao-cli@latest`
|
|
120
|
+
|
|
121
|
+
适合场景:
|
|
122
|
+
- 给团队成员提供统一入口
|
|
123
|
+
- 给非开发用户提供更低门槛的禅道操作方式
|
|
124
|
+
- 做内部推广和标准化接入
|
|
125
|
+
|
|
126
|
+
## 当前 MVP 能力
|
|
127
|
+
|
|
128
|
+
- token 登录:仅在明确鉴权失败时才从明文密码回退到 MD5
|
|
129
|
+
- 登录错误分类:区分账号密码错误、token 接口不存在、网络失败、服务端异常
|
|
130
|
+
- 自动清洗 PHP warning + JSON 混合响应
|
|
131
|
+
- `getMyTasks`
|
|
132
|
+
- `getTaskDetail`
|
|
133
|
+
- `getBugDetail`
|
|
134
|
+
- `getStoryDetail`
|
|
135
|
+
- `getExecutionDetail`
|
|
136
|
+
- `getExecutionBugs`
|
|
137
|
+
- `getMyBugs`
|
|
138
|
+
- `getProductBugs`
|
|
139
|
+
- `getComments`
|
|
140
|
+
- 写操作 preview:`updateTask`、`finishTask`、`resolveBug`、`addComment`
|
|
141
|
+
- `getMyProfile`
|
|
142
|
+
- `getMyTaskStatistics`
|
|
143
|
+
- `getMyBugStatistics`
|
|
144
|
+
- `getStoryRelatedBugs`
|
|
145
|
+
- `getBugRelatedStory`
|
|
146
|
+
- `getDevelopmentContext`
|
|
147
|
+
- `getProducts` / `getProductDetail`
|
|
148
|
+
- `getProjects` / `getProjectDetail`
|
|
149
|
+
- `getProjectExecutions` / `getExecutionBuilds`
|
|
150
|
+
- `getProductStories`
|
|
151
|
+
- `getProductTestCases` / `getTestCaseDetail`
|
|
152
|
+
- `getTestTasks` / `getTestTaskDetail`
|
|
153
|
+
- `getPrograms` / `getProgramDetail`
|
|
154
|
+
- `getProductPlans` / `getPlanDetail`
|
|
155
|
+
- `getProjectBuilds` / `getBuildDetail`
|
|
156
|
+
- `getProjectReleases`
|
|
157
|
+
- `searchStories`
|
|
158
|
+
- `searchStoriesByProductName`
|
|
159
|
+
- `updateStory` / `changeStory`(默认 preview)
|
|
160
|
+
- `createTaskFromStory` / `createTaskFromBug`(默认 preview)
|
|
161
|
+
- `linkStoriesToPlan` / `unlinkStoriesFromPlan`(默认 preview)
|
|
162
|
+
- `linkBugsToPlan` / `unlinkBugsFromPlan`(默认 preview)
|
|
163
|
+
- `updateExecution` / `startExecution` / `closeExecution` / `suspendExecution` / `activateExecution` / `putoffExecution`(默认 preview)
|
|
164
|
+
- `createBuild` / `updateBuild`(默认 preview)
|
|
165
|
+
- `createTestCase` / `updateTestCase`(默认 preview)
|
|
166
|
+
- `createTestTask` / `updateTestTask`(默认 preview)
|
|
167
|
+
|
|
168
|
+
`updateExecution` 按禅道 18.5 REST v1 文档/源码需要保留/传入:
|
|
169
|
+
|
|
170
|
+
- `project`
|
|
171
|
+
- `name`
|
|
172
|
+
- `code`
|
|
173
|
+
- `begin`
|
|
174
|
+
- `end`
|
|
175
|
+
|
|
176
|
+
注意:`updateExecution` 在禅道 18.5 且后台启用代号时存在服务端源码问题:`api/v1/entries/execution.php` 中 `$fields .= 'code'` 缺少前置逗号,导致请求体里的 `code` 不会进入服务端 `$_POST`,服务端会误报 `『迭代代号』不能为空。`。客户端无法稳定绕过;工具仍保留,但调用时会直接返回该诊断错误,不做真实写入。
|
|
177
|
+
|
|
178
|
+
`createBuild` 当前按禅道 18.5 实机/参考包契约使用 `POST /projects/{project}/builds`,通常需要提供:
|
|
179
|
+
|
|
180
|
+
- `project`
|
|
181
|
+
- `execution`
|
|
182
|
+
- `product`
|
|
183
|
+
- `name`
|
|
184
|
+
- `builder`
|
|
185
|
+
|
|
186
|
+
补充说明:
|
|
187
|
+
|
|
188
|
+
- `createTaskFromStory` / `createTaskFromBug` 在当前实例上实机验证发现通常需要额外提供:
|
|
189
|
+
- `assignedTo`
|
|
190
|
+
- `estStarted`
|
|
191
|
+
- `deadline`
|
|
192
|
+
- `createTaskFromBug` 当前只保证:
|
|
193
|
+
- 基于 Bug 预填任务内容并尝试创建任务
|
|
194
|
+
- 不保证创建后自动建立 Bug 关联
|
|
195
|
+
- 角色工具过滤:`full` / `dev` / `qa` / `pm`
|
|
196
|
+
- 写操作保护 helper:默认禁用写操作,后续写工具必须配合 `ZENTAO_ENABLE_WRITE=true` 和 `confirm: true`;已知 18.5 不支持或服务端有缺陷的写工具会保留入口,但调用时直接返回明确诊断错误。
|
|
197
|
+
- Phase 3B 写工具(execution / build / testcase / testtask)默认同样返回 preview,真实执行仍需 `ZENTAO_ENABLE_WRITE=true` + `confirm=true`:
|
|
198
|
+
- execution:`updateExecution` / `startExecution` / `closeExecution` / `suspendExecution` / `activateExecution` / `putoffExecution`
|
|
199
|
+
- build:`createBuild` / `updateBuild`
|
|
200
|
+
- testcase:`createTestCase` / `updateTestCase`
|
|
201
|
+
- testtask:`createTestTask` / `updateTestTask`
|
|
202
|
+
- 角色权限:
|
|
203
|
+
- `full` 拥有 `execution-write` + `build-write` + `testcase-write` + `testtask-write`
|
|
204
|
+
- `dev` 拥有 `execution-write` + `build-write`
|
|
205
|
+
- `pm` 仅拥有 `execution-write`
|
|
206
|
+
- `qa` 拥有 `testcase-write` + `testtask-write`
|
|
207
|
+
- `createTestCase` / `updateTestCase` 当前按禅道 18.5 REST v1 契约:
|
|
208
|
+
- 创建路径:`POST /products/{productId}/testcases`
|
|
209
|
+
- 更新路径:`PUT /testcases/{testCaseId}`
|
|
210
|
+
- `steps` 数组每项形如 `{ desc, expect, type? }`,`type` 支持 `step` / `item` / `group`,默认 `step`
|
|
211
|
+
- `module` / `story` 允许传 `0` 表示无模块 / 无关联需求
|
|
212
|
+
- `project` / `execution` / `keywords` 不是禅道 18.5 REST v1 用例创建/更新 entry 接收字段,传入会被服务端忽略;18.5 v1 创建的是产品用例,不会直接绑定项目/执行。
|
|
213
|
+
- `createTestTask` 当前按禅道 18.5 REST v1 契约:
|
|
214
|
+
- 创建路径:`POST /projects/{project}/testtasks`
|
|
215
|
+
- 创建必填:`project` / `productID` / `name` / `build` / `begin` / `end`,`begin` / `end` 格式 `YYYY-MM-DD`
|
|
216
|
+
- 创建请求体实际使用 `product` 字段,工具入参保留 `productID` 并在 API 层映射
|
|
217
|
+
- `build` 兼容数字 ID 和字符串 ID
|
|
218
|
+
- `createTestTask` 当前实例真实写验证通过
|
|
219
|
+
- `updateTestTask`:禅道 18.5 v1 `api/v1/entries/testtask.php` 只有 `get/delete`,没有 `put` 更新入口。工具仍保留,但调用时会直接返回该诊断错误,不做真实写入。
|
|
220
|
+
- `linkBugsToPlan` 已按 18.5 源码修正为 `POST /productplans/{planId}/linkbugs`。
|
|
221
|
+
- `finishTask` 已按 18.5 源码改为 `POST /tasks/{taskId}/finish`,需要 `currentConsumed` / `realStarted` / `finishedDate`。
|
|
222
|
+
- `addComment`:禅道 18.5 v1 没有 `comment/comments` entry,当前只支持 `getComments` 从对象详情 actions 回退读取;`addComment` 工具仍保留,但调用时会直接返回该诊断错误。
|
|
223
|
+
- token 失效后自动重试一次
|
|
224
|
+
- 可选 `ZENTAO_API_BASE_URL`:用于非标准 `/zentao/api.php/v1` 部署
|
|
225
|
+
|
|
226
|
+
## 配置
|
|
227
|
+
|
|
228
|
+
说明:
|
|
229
|
+
|
|
230
|
+
- `getMyBugs` 当前默认语义是“跨所有产品指派给我的 Bug”;传 `productId` 时收窄到指定产品。
|
|
231
|
+
- `getMyBugStatistics` 默认统计跨所有产品指派给我的 Bug;传 `productId` 时收窄到指定产品。
|
|
232
|
+
- `getComments` 在禅道 18.5 上会 fallback 到对象详情里的 `actions`,返回结果会明确标注 `source: "actions-fallback"`。
|
|
233
|
+
- `updateTask` / `finishTask` 不会默认修改 `assignedTo`,只有显式传入才会更新指派人。
|
|
234
|
+
|
|
235
|
+
支持环境变量:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
export ZENTAO_URL="https://zentao.cloudglab.cn"
|
|
239
|
+
export ZENTAO_USERNAME="your-account"
|
|
240
|
+
export ZENTAO_PASSWORD="your-password"
|
|
241
|
+
export ZENTAO_API_VERSION="v1"
|
|
242
|
+
# 可选:非标准部署时直接指定完整 API 基础地址
|
|
243
|
+
# export ZENTAO_API_BASE_URL="https://zentao.cloudglab.cn/custom/api.php/v1"
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
注意:`ZENTAO_URL` 传根域名即可,不要带 `/zentao`。
|
|
247
|
+
|
|
248
|
+
`initZentao` 现在默认只校验并在当前进程内生效,不会自动把密码写入 `~/.zentao/config.json`。
|
|
249
|
+
只有显式传 `save: true` 才会落盘。
|
|
250
|
+
|
|
251
|
+
## 开发
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
pnpm install
|
|
255
|
+
pnpm typecheck
|
|
256
|
+
pnpm build
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## 发布链路
|
|
260
|
+
|
|
261
|
+
已内置 GitHub Actions:当你 push 形如 `v*` 的 tag 时,会自动:
|
|
262
|
+
|
|
263
|
+
1. 安装依赖
|
|
264
|
+
2. 执行 `pnpm typecheck`
|
|
265
|
+
3. 执行 `pnpm build`
|
|
266
|
+
4. 发布 npm 包到 registry
|
|
267
|
+
5. 自动创建对应 GitHub Release
|
|
268
|
+
|
|
269
|
+
工作流文件:
|
|
270
|
+
|
|
271
|
+
- `.github/workflows/publish.yml`
|
|
272
|
+
|
|
273
|
+
### 使用前准备
|
|
274
|
+
|
|
275
|
+
1. 先把代码推到 GitHub 仓库
|
|
276
|
+
2. 在 npm 上为 `@cloudglab/zentao-cli` 配置 GitHub Actions trusted publisher,绑定仓库 `cloudglab/zentao-cli`
|
|
277
|
+
3. 确保 `package.json` 中的 `version` 与你要打的 tag 一致
|
|
278
|
+
|
|
279
|
+
### 发版方式
|
|
280
|
+
|
|
281
|
+
例如当前 `package.json` 版本是 `0.1.0`,则发版命令:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
git tag v0.1.0
|
|
285
|
+
git push origin v0.1.0
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
工作流会校验:
|
|
289
|
+
|
|
290
|
+
- tag `v0.1.0` 是否与 `package.json` 中的 `0.1.0` 一致
|
|
291
|
+
|
|
292
|
+
如果不一致,Action 会直接失败,避免把错误版本发布到 npm。
|
|
293
|
+
|
|
294
|
+
### 推荐顺序
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
# 1. 修改 package.json version
|
|
298
|
+
# 2. 提交代码
|
|
299
|
+
# 3. 推送分支
|
|
300
|
+
# 4. 打 tag
|
|
301
|
+
git tag v0.1.0
|
|
302
|
+
git push origin main
|
|
303
|
+
git push origin v0.1.0
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### 当前还未自动完成的部分
|
|
307
|
+
|
|
308
|
+
下面这些仍需要你首次手动完成一次:
|
|
309
|
+
|
|
310
|
+
- 初始化本地 git 仓库
|
|
311
|
+
- 首次 commit
|
|
312
|
+
- 创建 GitHub 仓库并 push 上去
|
|
313
|
+
- 在 npm 包侧完成 trusted publisher 绑定
|
|
314
|
+
|
|
315
|
+
## 启动
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
pnpm dev
|
|
319
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ZentaoHttpClient } from '../core/http.js';
|
|
2
|
+
import { type PaginationInput } from '../core/pagination.js';
|
|
3
|
+
import type { ZentaoBug } from '../types/zentao.js';
|
|
4
|
+
export interface BugListParams extends PaginationInput {
|
|
5
|
+
productId: number;
|
|
6
|
+
status?: string;
|
|
7
|
+
branch?: string;
|
|
8
|
+
order?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ResolveBugInput {
|
|
11
|
+
resolution: 'fixed' | 'bydesign' | 'duplicate' | 'external' | 'notrepro' | 'postponed' | 'willnotfix';
|
|
12
|
+
resolvedBuild?: string;
|
|
13
|
+
resolvedDate?: string;
|
|
14
|
+
assignedTo?: string;
|
|
15
|
+
comment?: string;
|
|
16
|
+
duplicateBug?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface MyBugListParams extends PaginationInput {
|
|
19
|
+
productId?: number;
|
|
20
|
+
branch?: string;
|
|
21
|
+
order?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare class BugApi {
|
|
24
|
+
private readonly http;
|
|
25
|
+
constructor(http: ZentaoHttpClient);
|
|
26
|
+
getMyBugs(params?: MyBugListParams): Promise<unknown>;
|
|
27
|
+
getProductBugs(params: BugListParams): Promise<unknown>;
|
|
28
|
+
getBugDetail(bugId: number): Promise<ZentaoBug>;
|
|
29
|
+
resolveBug(bugId: number, input: ResolveBugInput): Promise<unknown>;
|
|
30
|
+
private getAllMyBugsInProduct;
|
|
31
|
+
private sortBugs;
|
|
32
|
+
}
|
package/dist/api/bug.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { toClientPaginatedListResult, toServerListResult } from '../core/list-result.js';
|
|
2
|
+
import { normalizePagination } from '../core/pagination.js';
|
|
3
|
+
export class BugApi {
|
|
4
|
+
constructor(http) {
|
|
5
|
+
this.http = http;
|
|
6
|
+
}
|
|
7
|
+
async getMyBugs(params = {}) {
|
|
8
|
+
if (params.productId) {
|
|
9
|
+
return this.getProductBugs({ ...params, productId: params.productId, status: 'assigntome' });
|
|
10
|
+
}
|
|
11
|
+
const response = await this.http.request('GET', '/products');
|
|
12
|
+
const productsResult = toServerListResult(response, ['products']);
|
|
13
|
+
const products = productsResult.items;
|
|
14
|
+
const allBugs = [];
|
|
15
|
+
for (const product of products) {
|
|
16
|
+
const productId = product.id;
|
|
17
|
+
if (!productId)
|
|
18
|
+
continue;
|
|
19
|
+
const bugs = await this.getAllMyBugsInProduct({
|
|
20
|
+
productId,
|
|
21
|
+
branch: params.branch,
|
|
22
|
+
order: params.order,
|
|
23
|
+
});
|
|
24
|
+
allBugs.push(...bugs.map((bug) => ({
|
|
25
|
+
...bug,
|
|
26
|
+
product: bug.product ?? productId,
|
|
27
|
+
productName: bug.productName ?? product.name,
|
|
28
|
+
})));
|
|
29
|
+
}
|
|
30
|
+
const sorted = this.sortBugs(allBugs, params.order);
|
|
31
|
+
const paginated = toClientPaginatedListResult({ bugs: sorted }, ['bugs'], params);
|
|
32
|
+
return {
|
|
33
|
+
...paginated,
|
|
34
|
+
scope: 'global-assigntome',
|
|
35
|
+
scannedProducts: products.length,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async getProductBugs(params) {
|
|
39
|
+
const pagination = normalizePagination(params);
|
|
40
|
+
const response = await this.http.request('GET', `/products/${params.productId}/bugs`, {
|
|
41
|
+
params: {
|
|
42
|
+
...pagination,
|
|
43
|
+
branch: params.branch ?? 'all',
|
|
44
|
+
order: params.order ?? 'id_desc',
|
|
45
|
+
status: params.status === 'all' ? undefined : params.status,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
return toServerListResult(response, ['bugs'], params);
|
|
49
|
+
}
|
|
50
|
+
async getBugDetail(bugId) {
|
|
51
|
+
return this.http.request('GET', `/bugs/${bugId}`);
|
|
52
|
+
}
|
|
53
|
+
async resolveBug(bugId, input) {
|
|
54
|
+
const payload = { ...input };
|
|
55
|
+
if (payload.resolution === 'fixed' && !payload.resolvedBuild)
|
|
56
|
+
payload.resolvedBuild = 'trunk';
|
|
57
|
+
if (payload.resolution === 'duplicate' && !payload.duplicateBug) {
|
|
58
|
+
throw new Error('resolution=duplicate 时必须提供 duplicateBug');
|
|
59
|
+
}
|
|
60
|
+
return this.http.request('POST', `/bugs/${bugId}/resolve`, { data: payload });
|
|
61
|
+
}
|
|
62
|
+
async getAllMyBugsInProduct(params) {
|
|
63
|
+
const limit = 100;
|
|
64
|
+
const firstPage = await this.getProductBugs({ ...params, status: 'assigntome', page: 1, limit });
|
|
65
|
+
const bugs = [...firstPage.items];
|
|
66
|
+
const total = firstPage.total ?? bugs.length;
|
|
67
|
+
const totalPages = Math.ceil(total / limit);
|
|
68
|
+
for (let page = 2; page <= totalPages; page += 1) {
|
|
69
|
+
const response = await this.getProductBugs({ ...params, status: 'assigntome', page, limit });
|
|
70
|
+
bugs.push(...response.items);
|
|
71
|
+
}
|
|
72
|
+
return bugs;
|
|
73
|
+
}
|
|
74
|
+
sortBugs(bugs, order) {
|
|
75
|
+
const normalizedOrder = (order ?? 'id_desc').toLowerCase();
|
|
76
|
+
const sorted = [...bugs];
|
|
77
|
+
if (normalizedOrder === 'id_asc') {
|
|
78
|
+
sorted.sort((a, b) => a.id - b.id);
|
|
79
|
+
return sorted;
|
|
80
|
+
}
|
|
81
|
+
sorted.sort((a, b) => b.id - a.id);
|
|
82
|
+
return sorted;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=bug.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bug.js","sourceRoot":"","sources":["../../src/api/bug.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ZentaoHttpClient } from '../core/http.js';
|
|
2
|
+
export interface CreateBuildInput {
|
|
3
|
+
project: number;
|
|
4
|
+
execution: number;
|
|
5
|
+
product: number;
|
|
6
|
+
branch?: number;
|
|
7
|
+
name: string;
|
|
8
|
+
builder: string;
|
|
9
|
+
date?: string;
|
|
10
|
+
desc?: string;
|
|
11
|
+
scmPath?: string;
|
|
12
|
+
filePath?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface UpdateBuildInput {
|
|
15
|
+
execution?: number;
|
|
16
|
+
product?: number;
|
|
17
|
+
name?: string;
|
|
18
|
+
builder?: string;
|
|
19
|
+
date?: string;
|
|
20
|
+
desc?: string;
|
|
21
|
+
scmPath?: string;
|
|
22
|
+
filePath?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare class BuildApi {
|
|
25
|
+
private readonly http;
|
|
26
|
+
constructor(http: ZentaoHttpClient);
|
|
27
|
+
getProjectBuilds(projectId: number): Promise<unknown>;
|
|
28
|
+
getBuildDetail(buildId: number): Promise<unknown>;
|
|
29
|
+
createBuild(payload: CreateBuildInput): Promise<unknown>;
|
|
30
|
+
updateBuild(buildId: number, update: UpdateBuildInput): Promise<unknown>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { toServerListResult } from '../core/list-result.js';
|
|
2
|
+
export class BuildApi {
|
|
3
|
+
constructor(http) {
|
|
4
|
+
this.http = http;
|
|
5
|
+
}
|
|
6
|
+
async getProjectBuilds(projectId) {
|
|
7
|
+
const response = await this.http.request('GET', `/projects/${projectId}/builds`);
|
|
8
|
+
return toServerListResult(response, ['builds']);
|
|
9
|
+
}
|
|
10
|
+
async getBuildDetail(buildId) {
|
|
11
|
+
return this.http.request('GET', `/builds/${buildId}`);
|
|
12
|
+
}
|
|
13
|
+
async createBuild(payload) {
|
|
14
|
+
const { project, ...build } = payload;
|
|
15
|
+
return this.http.request('POST', `/projects/${project}/builds`, { data: build });
|
|
16
|
+
}
|
|
17
|
+
async updateBuild(buildId, update) {
|
|
18
|
+
return this.http.request('PUT', `/builds/${buildId}`, { data: update });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/api/build.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AA0B5D,MAAM,OAAO,QAAQ;IACnB,YAA6B,IAAsB;QAAtB,SAAI,GAAJ,IAAI,CAAkB;IAAG,CAAC;IAEvD,KAAK,CAAC,gBAAgB,CAAC,SAAiB;QACtC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,aAAa,SAAS,SAAS,CAAC,CAAC;QACjF,OAAO,kBAAkB,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,OAAe;QAClC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,WAAW,OAAO,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;QACtC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,aAAa,OAAO,SAAS,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACnF,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAe,EAAE,MAAwB;QACzD,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,WAAW,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1E,CAAC;CACF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ZentaoHttpClient } from '../core/http.js';
|
|
2
|
+
export type CommentObjectType = 'task' | 'bug' | 'story' | 'product' | 'project' | 'execution' | string;
|
|
3
|
+
export interface AddCommentInput {
|
|
4
|
+
objectType: CommentObjectType;
|
|
5
|
+
objectID: number;
|
|
6
|
+
comment: string;
|
|
7
|
+
}
|
|
8
|
+
export interface CommentsFallbackResult {
|
|
9
|
+
source: 'actions-fallback';
|
|
10
|
+
note: string;
|
|
11
|
+
actions: unknown;
|
|
12
|
+
}
|
|
13
|
+
export declare class CommentApi {
|
|
14
|
+
private readonly http;
|
|
15
|
+
constructor(http: ZentaoHttpClient);
|
|
16
|
+
getComments(objectType: CommentObjectType, objectID: number): Promise<unknown>;
|
|
17
|
+
addComment(input: AddCommentInput): Promise<unknown>;
|
|
18
|
+
private getActionsFromObjectDetail;
|
|
19
|
+
private getObjectEndpoint;
|
|
20
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export class CommentApi {
|
|
2
|
+
constructor(http) {
|
|
3
|
+
this.http = http;
|
|
4
|
+
}
|
|
5
|
+
async getComments(objectType, objectID) {
|
|
6
|
+
try {
|
|
7
|
+
return await this.http.request('GET', `/comments/${objectType}/${objectID}`);
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
if (error instanceof Error && error.message.includes('404')) {
|
|
11
|
+
return this.getActionsFromObjectDetail(objectType, objectID);
|
|
12
|
+
}
|
|
13
|
+
throw error;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async addComment(input) {
|
|
17
|
+
return this.http.request('POST', '/comment', { data: input });
|
|
18
|
+
}
|
|
19
|
+
async getActionsFromObjectDetail(objectType, objectID) {
|
|
20
|
+
const endpoint = this.getObjectEndpoint(objectType, objectID);
|
|
21
|
+
const detail = await this.http.request('GET', endpoint);
|
|
22
|
+
return {
|
|
23
|
+
source: 'actions-fallback',
|
|
24
|
+
note: '当前禅道版本不支持 /comments/{objectType}/{objectID},这里返回对象详情中的 actions,其中包含评论以及状态变更等操作记录。',
|
|
25
|
+
actions: detail.actions ?? [],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
getObjectEndpoint(objectType, objectID) {
|
|
29
|
+
switch (objectType) {
|
|
30
|
+
case 'task':
|
|
31
|
+
return `/tasks/${objectID}`;
|
|
32
|
+
case 'bug':
|
|
33
|
+
return `/bugs/${objectID}`;
|
|
34
|
+
case 'story':
|
|
35
|
+
return `/stories/${objectID}`;
|
|
36
|
+
case 'product':
|
|
37
|
+
return `/products/${objectID}`;
|
|
38
|
+
case 'project':
|
|
39
|
+
return `/projects/${objectID}`;
|
|
40
|
+
case 'execution':
|
|
41
|
+
return `/executions/${objectID}`;
|
|
42
|
+
default:
|
|
43
|
+
throw new Error(`不支持从详情回退获取评论: ${objectType}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=comment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comment.js","sourceRoot":"","sources":["../../src/api/comment.ts"],"names":[],"mappings":"AAgBA,MAAM,OAAO,UAAU;IACrB,YAA6B,IAAsB;QAAtB,SAAI,GAAJ,IAAI,CAAkB;IAAG,CAAC;IAEvD,KAAK,CAAC,WAAW,CAAC,UAA6B,EAAE,QAAgB;QAC/D,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,aAAa,UAAU,IAAI,QAAQ,EAAE,CAAC,CAAC;QAC/E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC5D,OAAO,IAAI,CAAC,0BAA0B,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YAC/D,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,KAAsB;QACrC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAChE,CAAC;IAEO,KAAK,CAAC,0BAA0B,CAAC,UAA6B,EAAE,QAAgB;QACtF,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC9D,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAA0B,KAAK,EAAE,QAAQ,CAAC,CAAC;QACjF,OAAO;YACL,MAAM,EAAE,kBAAkB;YAC1B,IAAI,EAAE,mFAAmF;YACzF,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE;SAC9B,CAAC;IACJ,CAAC;IAEO,iBAAiB,CAAC,UAA6B,EAAE,QAAgB;QACvE,QAAQ,UAAU,EAAE,CAAC;YACnB,KAAK,MAAM;gBACT,OAAO,UAAU,QAAQ,EAAE,CAAC;YAC9B,KAAK,KAAK;gBACR,OAAO,SAAS,QAAQ,EAAE,CAAC;YAC7B,KAAK,OAAO;gBACV,OAAO,YAAY,QAAQ,EAAE,CAAC;YAChC,KAAK,SAAS;gBACZ,OAAO,aAAa,QAAQ,EAAE,CAAC;YACjC,KAAK,SAAS;gBACZ,OAAO,aAAa,QAAQ,EAAE,CAAC;YACjC,KAAK,WAAW;gBACd,OAAO,eAAe,QAAQ,EAAE,CAAC;YACnC;gBACE,MAAM,IAAI,KAAK,CAAC,iBAAiB,UAAU,EAAE,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { BugApi } from './bug.js';
|
|
2
|
+
import type { RelationApi } from './relation.js';
|
|
3
|
+
import type { StoryApi } from './story.js';
|
|
4
|
+
export declare class DevelopmentContextApi {
|
|
5
|
+
private readonly bugApi;
|
|
6
|
+
private readonly storyApi;
|
|
7
|
+
private readonly relationApi;
|
|
8
|
+
constructor(bugApi: BugApi, storyApi: StoryApi, relationApi: RelationApi);
|
|
9
|
+
getDevelopmentContext(input: {
|
|
10
|
+
entityType: 'story' | 'bug';
|
|
11
|
+
entityId: number;
|
|
12
|
+
productId?: number;
|
|
13
|
+
}): Promise<unknown>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export class DevelopmentContextApi {
|
|
2
|
+
constructor(bugApi, storyApi, relationApi) {
|
|
3
|
+
this.bugApi = bugApi;
|
|
4
|
+
this.storyApi = storyApi;
|
|
5
|
+
this.relationApi = relationApi;
|
|
6
|
+
}
|
|
7
|
+
async getDevelopmentContext(input) {
|
|
8
|
+
if (input.entityType === 'story') {
|
|
9
|
+
const story = await this.storyApi.getStoryDetail(input.entityId);
|
|
10
|
+
const relatedBugs = await this.relationApi.getStoryRelatedBugs(input.entityId, input.productId);
|
|
11
|
+
const testCases = Array.isArray(story.cases) ? story.cases : [];
|
|
12
|
+
return {
|
|
13
|
+
entityType: 'story',
|
|
14
|
+
story,
|
|
15
|
+
relatedBugs,
|
|
16
|
+
testCases,
|
|
17
|
+
summary: {
|
|
18
|
+
relatedBugsCount: relatedBugs.bugs.length,
|
|
19
|
+
testCasesCount: testCases.length,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const bug = await this.bugApi.getBugDetail(input.entityId);
|
|
24
|
+
const relatedStory = await this.relationApi.getBugRelatedStory(input.entityId);
|
|
25
|
+
return {
|
|
26
|
+
entityType: 'bug',
|
|
27
|
+
bug,
|
|
28
|
+
relatedStory,
|
|
29
|
+
summary: {
|
|
30
|
+
hasRelatedStory: relatedStory !== null,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=development-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"development-context.js","sourceRoot":"","sources":["../../src/api/development-context.ts"],"names":[],"mappings":"AAIA,MAAM,OAAO,qBAAqB;IAChC,YACmB,MAAc,EACd,QAAkB,EAClB,WAAwB;QAFxB,WAAM,GAAN,MAAM,CAAQ;QACd,aAAQ,GAAR,QAAQ,CAAU;QAClB,gBAAW,GAAX,WAAW,CAAa;IACxC,CAAC;IAEJ,KAAK,CAAC,qBAAqB,CAAC,KAA4E;QACtG,IAAI,KAAK,CAAC,UAAU,KAAK,OAAO,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YACjE,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;YAChG,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YAEhE,OAAO;gBACL,UAAU,EAAE,OAAO;gBACnB,KAAK;gBACL,WAAW;gBACX,SAAS;gBACT,OAAO,EAAE;oBACP,gBAAgB,EAAE,WAAW,CAAC,IAAI,CAAC,MAAM;oBACzC,cAAc,EAAE,SAAS,CAAC,MAAM;iBACjC;aACF,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC3D,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAE/E,OAAO;YACL,UAAU,EAAE,KAAK;YACjB,GAAG;YACH,YAAY;YACZ,OAAO,EAAE;gBACP,eAAe,EAAE,YAAY,KAAK,IAAI;aACvC;SACF,CAAC;IACJ,CAAC;CACF"}
|