zentao-api 0.5.1 → 0.5.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/README.md +258 -188
- package/dist/browser/zentao-api.global.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/modules/export.d.ts +33 -0
- package/dist/modules/export.js +42 -0
- package/dist/modules/registry.d.ts +1 -0
- package/dist/modules/registry.js +1 -0
- package/dist/request/index.js +3 -3
- package/dist/types/data.d.ts +6 -6
- package/dist/types/options.d.ts +3 -1
- package/dist/utils/data.d.ts +3 -2
- package/dist/utils/data.js +0 -0
- package/dist/version.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,308 +1,378 @@
|
|
|
1
1
|
# zentao-api
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/zentao-api)
|
|
4
|
-
[](https://nodejs.org)
|
|
5
|
+
[](https://github.com/easysoft/zentao-api/blob/main/LICENSE)
|
|
6
6
|
|
|
7
7
|
Browser & Node.js SDK for [ZenTao](https://www.zentao.net) (禅道) API v2.
|
|
8
8
|
|
|
9
|
-
`zentao-api`
|
|
9
|
+
`zentao-api` 是一个零运行时依赖的 JavaScript/TypeScript SDK,提供底层 REST 客户端和基于模块注册表的高阶请求接口,可运行在 Node.js 18+、Bun、浏览器打包工具及 CDN/script 标签环境中。
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
[快速开始](#快速开始) · [调用方式](#两种调用方式) · [浏览器](#浏览器) · [完整文档](#文档)
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## 特性
|
|
14
|
+
|
|
15
|
+
- 两层 API:使用 `ZentaoClient` 直接调用 REST 路径,或使用 `request("module/action")` 自动组装路径、查询参数和请求体。
|
|
16
|
+
- 完整类型提示:内置请求名、参数和 `data` 返回值可由 TypeScript 自动推导。
|
|
17
|
+
- 统一响应结构:自动提取业务数据和分页信息,稳定返回 `ResponseData<T>`。
|
|
18
|
+
- 覆盖禅道常用模块:产品、项目、执行、需求、任务、Bug、测试、版本、发布等。
|
|
19
|
+
- 内置本地数据处理:支持转换、过滤、搜索、排序、限制数量和字段摘取。
|
|
20
|
+
- 可扩展模块注册表、持久化 Profile、稳定错误码及 Node.js 自签名证书支持。
|
|
21
|
+
|
|
22
|
+
## 安装
|
|
14
23
|
|
|
15
24
|
```sh
|
|
16
25
|
npm install zentao-api
|
|
17
26
|
```
|
|
18
27
|
|
|
19
|
-
|
|
28
|
+
使用 Bun:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
bun add zentao-api
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
包采用 ESM,并自带 TypeScript 类型定义。Node.js 需要 18 或更高版本。
|
|
20
35
|
|
|
21
|
-
|
|
36
|
+
## 快速开始
|
|
37
|
+
|
|
38
|
+
推荐通过 `ZentaoClient.init()` 配置全局客户端,再使用高阶 `request()` 调用内置模块:
|
|
22
39
|
|
|
23
40
|
```ts
|
|
24
|
-
import { ZentaoClient } from 'zentao-api';
|
|
41
|
+
import { ZentaoClient, request } from 'zentao-api';
|
|
25
42
|
|
|
26
|
-
|
|
43
|
+
ZentaoClient.init({
|
|
27
44
|
baseUrl: 'https://zentao.example.com',
|
|
28
45
|
token: 'your-token',
|
|
29
46
|
});
|
|
30
47
|
|
|
31
|
-
const
|
|
48
|
+
const result = await request('product/list', {
|
|
49
|
+
browseType: 'all',
|
|
50
|
+
recPerPage: 20,
|
|
51
|
+
pageID: 1,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
console.log(result.data); // 产品列表
|
|
55
|
+
console.log(result.pager?.total); // 总记录数
|
|
32
56
|
```
|
|
33
57
|
|
|
34
|
-
`baseUrl`
|
|
58
|
+
`baseUrl` 填写禅道站点根地址;SDK 会自动拼接 `/api.php/v2`,并在后续请求中注入 `Token` 请求头。
|
|
35
59
|
|
|
36
|
-
###
|
|
60
|
+
### 使用账号密码登录
|
|
37
61
|
|
|
38
|
-
|
|
62
|
+
没有 token 时,可以先登录。`ZentaoClient.init()` 返回的实例同时也是 `request()` 使用的全局客户端:
|
|
39
63
|
|
|
40
64
|
```ts
|
|
41
|
-
|
|
42
|
-
const token = await client.login('admin', 'password');
|
|
43
|
-
```
|
|
65
|
+
import { ZentaoClient, request } from 'zentao-api';
|
|
44
66
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
```ts
|
|
48
|
-
import { ZentaoClient, request, setGlobalOptions } from 'zentao-api';
|
|
49
|
-
|
|
50
|
-
ZentaoClient.init({
|
|
67
|
+
const client = ZentaoClient.init({
|
|
51
68
|
baseUrl: 'https://zentao.example.com',
|
|
52
|
-
token: 'your-token',
|
|
53
69
|
});
|
|
54
70
|
|
|
55
|
-
|
|
71
|
+
await client.login('admin', 'password');
|
|
56
72
|
|
|
57
|
-
const
|
|
73
|
+
const products = await request('product/list');
|
|
58
74
|
```
|
|
59
75
|
|
|
76
|
+
请从环境变量或安全配置中读取账号、密码和 token,不要将凭据提交到代码仓库。
|
|
77
|
+
|
|
78
|
+
## 两种调用方式
|
|
79
|
+
|
|
80
|
+
| 调用方式 | 适合场景 | 返回值 |
|
|
81
|
+
| --- | --- | --- |
|
|
82
|
+
| `request("module/action")` | 调用注册表中的常用禅道 API,自动处理参数与分页 | 统一的 `ResponseData<T>` |
|
|
83
|
+
| `ZentaoClient` | 调用尚未注册的路径、上传文件或读取二进制响应 | 禅道原始响应体 |
|
|
84
|
+
|
|
85
|
+
### 高阶模块请求
|
|
86
|
+
|
|
60
87
|
请求名支持三种写法:
|
|
61
88
|
|
|
62
89
|
```ts
|
|
63
|
-
await request('product'); //
|
|
64
|
-
await request('product/list'); //
|
|
65
|
-
await request('product/1'); //
|
|
90
|
+
await request('product'); // product/list
|
|
91
|
+
await request('product/list'); // 显式动作名
|
|
92
|
+
await request('product/1'); // product/get,且对象 ID 为 1
|
|
66
93
|
```
|
|
67
94
|
|
|
68
|
-
|
|
95
|
+
带作用域的列表可以传产品、项目或执行 ID,SDK 会自动选择实际路径:
|
|
69
96
|
|
|
70
97
|
```ts
|
|
71
|
-
const
|
|
72
|
-
|
|
98
|
+
const bugs = await request('bug/list', {
|
|
99
|
+
productID: 1,
|
|
100
|
+
browseType: 'unclosed',
|
|
101
|
+
});
|
|
73
102
|
|
|
74
|
-
|
|
103
|
+
// 也可以显式指定作用域:
|
|
104
|
+
const projectBugs = await request('bug/list', {
|
|
105
|
+
scope: 'projects',
|
|
106
|
+
scopeID: 8,
|
|
107
|
+
});
|
|
108
|
+
```
|
|
75
109
|
|
|
76
|
-
SDK
|
|
110
|
+
单次调用选项会覆盖全局选项。下面的处理只作用于 SDK 返回的 `data`,不会改变服务端数据:
|
|
77
111
|
|
|
78
112
|
```ts
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
113
|
+
const bugs = await request(
|
|
114
|
+
'bug/list',
|
|
115
|
+
{ productID: 1, recPerPage: 100 },
|
|
116
|
+
{
|
|
117
|
+
filter: ['status=active,pri>=2'],
|
|
118
|
+
search: ['登录'],
|
|
119
|
+
sort: 'pri:desc,id:asc',
|
|
120
|
+
limit: '10',
|
|
121
|
+
pick: ['id', 'title', 'pri'],
|
|
122
|
+
},
|
|
123
|
+
);
|
|
82
124
|
```
|
|
83
125
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
### ZentaoClient
|
|
87
|
-
|
|
88
|
-
| 方法 | 说明 |
|
|
89
|
-
|------|------|
|
|
90
|
-
| `client.get<T>(path)` | GET 请求 |
|
|
91
|
-
| `client.post<T>(path, body)` | POST 请求 |
|
|
92
|
-
| `client.put<T>(path, body)` | PUT 请求 |
|
|
93
|
-
| `client.delete<T>(path)` | DELETE 请求 |
|
|
94
|
-
| `client.login(account, password)` | 账号密码登录,返回 token |
|
|
95
|
-
| `client.request(path, options?)` | 通用请求(底层方法) |
|
|
96
|
-
| `ZentaoClient.init(options)` | 创建全局单例客户端 |
|
|
97
|
-
| `ZentaoClient.create(options)` | 工厂方法创建客户端 |
|
|
98
|
-
| `ZentaoClient.fromProfile(key?)` | 从持久化 profile 恢复客户端 |
|
|
99
|
-
|
|
100
|
-
### 模块请求
|
|
101
|
-
|
|
102
|
-
| 函数 | 说明 |
|
|
103
|
-
|------|------|
|
|
104
|
-
| `request(name, params?, options?)` | 按 `"module"`、`"module/action"` 或 `"module/<objectID>"` 调用已注册模块 |
|
|
105
|
-
| `defineModules(modules, options?)` | 注册或扩展模块定义 |
|
|
106
|
-
| `defineModuleActions(module, actions)` | 为已有模块追加或替换动作 |
|
|
107
|
-
| `extendModuleAction(module, action, patch)` | 深度合并补丁,或用回调改写已有动作 |
|
|
108
|
-
| `getModule(name)` | 获取模块定义 |
|
|
109
|
-
| `getModuleAction(module, action)` | 获取指定动作定义 |
|
|
110
|
-
| `getModuleNames()` | 获取所有已注册模块名 |
|
|
111
|
-
| `setGlobalOptions(options)` | 设置全局默认选项 |
|
|
112
|
-
| `getGlobalOptions()` | 获取当前全局选项 |
|
|
126
|
+
更多过滤语法和处理顺序见[本地数据处理指南](https://github.com/easysoft/zentao-api/blob/main/docs/guide/data-processing.md)。
|
|
113
127
|
|
|
114
|
-
|
|
128
|
+
### 统一返回结构
|
|
115
129
|
|
|
116
|
-
|
|
130
|
+
除非启用 `raw`,`request()` 始终返回以下结构:
|
|
117
131
|
|
|
118
132
|
```ts
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
133
|
+
interface ResponseData<T> {
|
|
134
|
+
status: 'success' | 'fail';
|
|
135
|
+
message?: string;
|
|
136
|
+
data?: T;
|
|
137
|
+
pager?: {
|
|
138
|
+
total: number;
|
|
139
|
+
page: number;
|
|
140
|
+
recPerPage: number;
|
|
141
|
+
};
|
|
128
142
|
}
|
|
129
143
|
```
|
|
130
144
|
|
|
131
|
-
|
|
145
|
+
内置请求会自动推导参数和数据类型;自定义调用也可以显式收窄 `data`:
|
|
132
146
|
|
|
133
|
-
|
|
147
|
+
```ts
|
|
148
|
+
interface ProductSummary {
|
|
149
|
+
id: number;
|
|
150
|
+
name: string;
|
|
151
|
+
}
|
|
134
152
|
|
|
135
|
-
|
|
153
|
+
const result = await request<ProductSummary[]>('product/list', {});
|
|
154
|
+
result.data?.forEach((product) => console.log(product.name));
|
|
155
|
+
```
|
|
136
156
|
|
|
137
|
-
|
|
157
|
+
需要完整服务端响应时,传入 `{ raw: true }`。此时会跳过响应归一化、本地数据处理和 `throwOnFail`:
|
|
138
158
|
|
|
139
159
|
```ts
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
defineModules({
|
|
143
|
-
name: 'custom',
|
|
144
|
-
actions: [
|
|
145
|
-
{
|
|
146
|
-
name: 'list',
|
|
147
|
-
type: 'list',
|
|
148
|
-
method: 'GET',
|
|
149
|
-
path: '/custom',
|
|
150
|
-
resultType: 'list',
|
|
151
|
-
resultGetter: 'items',
|
|
152
|
-
},
|
|
153
|
-
],
|
|
154
|
-
});
|
|
160
|
+
const raw = await request('product/list', {}, { raw: true });
|
|
155
161
|
```
|
|
156
162
|
|
|
157
|
-
###
|
|
163
|
+
### 底层 REST 客户端
|
|
164
|
+
|
|
165
|
+
`ZentaoClient` 适合直接调用 API v2 路径:
|
|
158
166
|
|
|
159
167
|
```ts
|
|
160
|
-
import {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
path: '/bugs/{bugID}/archive',
|
|
167
|
-
pathParams: { bugID: 'Bug ID' },
|
|
168
|
-
resultType: 'text',
|
|
168
|
+
import { ZentaoClient } from 'zentao-api';
|
|
169
|
+
|
|
170
|
+
const client = new ZentaoClient({
|
|
171
|
+
baseUrl: 'https://zentao.example.com',
|
|
172
|
+
token: 'your-token',
|
|
173
|
+
timeout: 10_000,
|
|
169
174
|
});
|
|
175
|
+
|
|
176
|
+
const products = await client.get('/products');
|
|
177
|
+
const product = await client.get('/products/1');
|
|
178
|
+
const created = await client.post('/products', { name: '新产品' });
|
|
170
179
|
```
|
|
171
180
|
|
|
172
|
-
|
|
181
|
+
通用 `client.request()` 还支持自定义请求头、查询参数、`AbortSignal`、`FormData`,以及 `text`、`arrayBuffer`、`blob`、`response` 等响应类型。
|
|
173
182
|
|
|
174
|
-
|
|
183
|
+
## 配置
|
|
175
184
|
|
|
176
|
-
|
|
177
|
-
import { extendModuleAction } from 'zentao-api';
|
|
185
|
+
### 客户端选项
|
|
178
186
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
187
|
+
| 选项 | 类型 | 说明 |
|
|
188
|
+
| --- | --- | --- |
|
|
189
|
+
| `baseUrl` | `string` | 禅道站点根地址;SDK 自动处理 `/api.php/v2`。 |
|
|
190
|
+
| `token` | `string` | 禅道 API Token;也可稍后通过 `login()` 获取。 |
|
|
191
|
+
| `timeout` | `number` | 默认请求超时时间,单位为毫秒,默认 `10000`。 |
|
|
192
|
+
| `insecure` | `boolean` | 跳过 TLS 证书校验,仅支持 Node.js 运行时。 |
|
|
193
|
+
|
|
194
|
+
### 全局选项
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
import { setGlobalOptions } from 'zentao-api';
|
|
198
|
+
|
|
199
|
+
setGlobalOptions({
|
|
200
|
+
recPerPage: '50',
|
|
201
|
+
limit: '20',
|
|
202
|
+
timeout: 30_000,
|
|
203
|
+
throwOnFail: true,
|
|
204
|
+
autoFill: false,
|
|
183
205
|
});
|
|
184
206
|
```
|
|
185
207
|
|
|
186
|
-
|
|
208
|
+
常用全局选项包括 `client`、`recPerPage`、`limit`、`timeout`、`insecure`、`persistProfiles`、`throwOnFail` 和 `autoFill`。优先级通常为:单次调用选项 > 全局选项 > 客户端默认值。
|
|
209
|
+
|
|
210
|
+
### 持久化 Profile
|
|
211
|
+
|
|
212
|
+
Profile 默认不会写入。先启用 `persistProfiles`,登录成功后才会保存站点、账号、token 和客户端配置:
|
|
187
213
|
|
|
188
214
|
```ts
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
215
|
+
import { ZentaoClient, setGlobalOptions } from 'zentao-api';
|
|
216
|
+
|
|
217
|
+
setGlobalOptions({ persistProfiles: true });
|
|
218
|
+
|
|
219
|
+
const client = ZentaoClient.init({
|
|
220
|
+
baseUrl: 'https://zentao.example.com',
|
|
195
221
|
});
|
|
196
|
-
```
|
|
197
222
|
|
|
198
|
-
|
|
223
|
+
await client.login('admin', 'password');
|
|
224
|
+
```
|
|
199
225
|
|
|
200
|
-
|
|
226
|
+
后续可以恢复当前 Profile;如需继续调用高阶 `request()`,再把恢复的客户端设为全局客户端:
|
|
201
227
|
|
|
202
228
|
```ts
|
|
203
|
-
|
|
229
|
+
const client = await ZentaoClient.fromProfile();
|
|
230
|
+
setGlobalOptions({ client });
|
|
231
|
+
|
|
232
|
+
// 或指定 profile key
|
|
233
|
+
const another = await ZentaoClient.fromProfile(
|
|
234
|
+
'admin@https://zentao.example.com',
|
|
235
|
+
);
|
|
204
236
|
```
|
|
205
237
|
|
|
206
|
-
|
|
238
|
+
| 环境 | 存储位置 |
|
|
239
|
+
| --- | --- |
|
|
240
|
+
| Node.js / Bun | `~/.config/zentao/zentao.json` |
|
|
241
|
+
| 浏览器 | `localStorage` |
|
|
207
242
|
|
|
208
|
-
|
|
243
|
+
Profile 包含可直接调用 API 的 token,请按敏感凭据保护其存储位置。
|
|
209
244
|
|
|
210
|
-
|
|
245
|
+
## 错误处理
|
|
246
|
+
|
|
247
|
+
HTTP、网络、超时、参数、模块解析和 Profile 错误会统一抛出带稳定错误码的 `ZentaoError`:
|
|
211
248
|
|
|
212
249
|
```ts
|
|
213
|
-
import
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
250
|
+
import { request, ZentaoError } from 'zentao-api';
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
await request('bug/resolve', {
|
|
254
|
+
bugID: 1001,
|
|
255
|
+
resolution: 'fixed',
|
|
256
|
+
}, {
|
|
257
|
+
throwOnFail: true,
|
|
258
|
+
});
|
|
259
|
+
} catch (error) {
|
|
260
|
+
if (error instanceof ZentaoError) {
|
|
261
|
+
console.error(error.code); // 例如 E_HTTP_ERROR、E_TIMEOUT
|
|
262
|
+
console.error(error.message);
|
|
263
|
+
console.error(error.details);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
220
266
|
```
|
|
221
267
|
|
|
268
|
+
禅道返回 `{ status: "fail" }` 属于业务失败,默认仍作为 `ResponseData` 返回;只有启用单次或全局 `throwOnFail` 后,才会抛出 `E_API_FAILED`。HTTP、网络和超时等传输层错误始终抛出异常。
|
|
269
|
+
|
|
222
270
|
## 浏览器
|
|
223
271
|
|
|
224
|
-
|
|
272
|
+
Vite、Webpack、Rspack 等打包工具可以从包根导入;需要显式选择浏览器入口时使用 `zentao-api/browser`:
|
|
225
273
|
|
|
226
274
|
```ts
|
|
227
|
-
import { ZentaoClient } from 'zentao-api';
|
|
275
|
+
import { ZentaoClient, request } from 'zentao-api/browser';
|
|
228
276
|
```
|
|
229
277
|
|
|
230
|
-
|
|
278
|
+
使用 script 标签时,UMD 构建会将公共 API 暴露到 `window.ZentaoAPI`:
|
|
231
279
|
|
|
232
280
|
```html
|
|
233
281
|
<script src="https://cdn.jsdelivr.net/npm/zentao-api@latest/dist/browser/zentao-api.global.js"></script>
|
|
234
282
|
<script>
|
|
235
|
-
|
|
236
|
-
|
|
283
|
+
const client = new window.ZentaoAPI.ZentaoClient({
|
|
284
|
+
baseUrl: 'https://zentao.example.com',
|
|
285
|
+
token: 'your-token',
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
console.log(window.ZentaoAPI.VERSION);
|
|
237
289
|
</script>
|
|
238
290
|
```
|
|
239
291
|
|
|
240
|
-
>
|
|
241
|
-
>
|
|
242
|
-
> **TLS**:`insecure` TLS 选项仅适用于 Node.js,在浏览器运行时会抛出错误。
|
|
292
|
+
> 浏览器直连要求禅道服务器允许 CORS,并会向前端暴露 token;敏感场景请通过后端代理。`insecure` 仅适用于 Node.js,在浏览器中使用会抛出 `E_INSECURE_BROWSER`。
|
|
243
293
|
|
|
244
|
-
##
|
|
294
|
+
## 模块注册表与扩展
|
|
245
295
|
|
|
246
|
-
|
|
296
|
+
可以在运行时查看 SDK 当前支持的模块、动作和参数:
|
|
247
297
|
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
298
|
+
```ts
|
|
299
|
+
import {
|
|
300
|
+
getModuleAction,
|
|
301
|
+
getModuleActionParams,
|
|
302
|
+
getModuleNames,
|
|
303
|
+
getObjectProps,
|
|
304
|
+
} from 'zentao-api';
|
|
305
|
+
|
|
306
|
+
const modules = getModuleNames();
|
|
307
|
+
const action = getModuleAction('bug', 'create');
|
|
308
|
+
const params = getModuleActionParams('bug', 'create');
|
|
309
|
+
const labels = getObjectProps('bug');
|
|
252
310
|
```
|
|
253
311
|
|
|
254
|
-
|
|
312
|
+
未注册的 API 可以新增为自定义模块:
|
|
255
313
|
|
|
256
|
-
|
|
314
|
+
```ts
|
|
315
|
+
import { defineModules } from 'zentao-api';
|
|
257
316
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
317
|
+
defineModules({
|
|
318
|
+
name: 'custom',
|
|
319
|
+
actions: [
|
|
320
|
+
{
|
|
321
|
+
name: 'list',
|
|
322
|
+
type: 'list',
|
|
323
|
+
path: '/custom',
|
|
324
|
+
resultGetter: 'items',
|
|
325
|
+
},
|
|
326
|
+
],
|
|
327
|
+
});
|
|
261
328
|
```
|
|
262
329
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
| 变量 | 说明 | 默认值 |
|
|
266
|
-
|------|------|--------|
|
|
267
|
-
| `ZENTAO_URL` | 禅道站点地址 | *(必填)* |
|
|
268
|
-
| `ZENTAO_ACCOUNT` | 登录账号 | *(必填)* |
|
|
269
|
-
| `ZENTAO_PASSWORD` | 登录密码 | *(必填)* |
|
|
270
|
-
| `ZENTAO_TOKEN` | 直接提供 Token(替代账号密码) | — |
|
|
271
|
-
| `ZENTAO_REVIEWER` | 需求评审人(未设置时复用 `ZENTAO_ACCOUNT`) | — |
|
|
272
|
-
| `ZENTAO_KEEP_TEST_DATA` | 保留临时测试数据 | `false` |
|
|
273
|
-
| `ZENTAO_TIMEOUT` | 请求超时(ms) | `30000` |
|
|
274
|
-
| `ZENTAO_INSECURE` | 跳过 TLS 证书验证 | `false` |
|
|
330
|
+
只需修改已有动作的个别字段时,使用 `extendModuleAction()`;补丁对象会深度合并,数组会整体替换:
|
|
275
331
|
|
|
276
|
-
|
|
332
|
+
```ts
|
|
333
|
+
import { extendModuleAction } from 'zentao-api';
|
|
277
334
|
|
|
335
|
+
extendModuleAction('task', 'list', {
|
|
336
|
+
path: '/executions/{executionID}/tasks',
|
|
337
|
+
pathParams: { executionID: '执行 ID' },
|
|
338
|
+
});
|
|
278
339
|
```
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
340
|
+
|
|
341
|
+
`defineModuleActions()` 可追加或整体替换单个动作,`defineModules(module, { replace: true })` 可整体替换同名模块。请确保扩展代码在第一次调用 `request()` 前执行。
|
|
342
|
+
|
|
343
|
+
## 文档
|
|
344
|
+
|
|
345
|
+
- [快速开始](https://github.com/easysoft/zentao-api/blob/main/docs/guide/index.md)
|
|
346
|
+
- [安装与配置](https://github.com/easysoft/zentao-api/blob/main/docs/guide/installation.md)
|
|
347
|
+
- [常见 API 示例](https://github.com/easysoft/zentao-api/blob/main/docs/guide/examples.md)
|
|
348
|
+
- [Profile 与错误处理](https://github.com/easysoft/zentao-api/blob/main/docs/guide/profiles-and-errors.md)
|
|
349
|
+
- [SDK API Reference](https://github.com/easysoft/zentao-api/tree/main/docs/reference)
|
|
350
|
+
- [ZenTao 模块与动作列表](https://github.com/easysoft/zentao-api/tree/main/docs/zentao-api)
|
|
351
|
+
- [变更日志](https://github.com/easysoft/zentao-api/blob/main/CHANGES.md)
|
|
352
|
+
|
|
353
|
+
## 开发与贡献
|
|
354
|
+
|
|
355
|
+
本仓库只使用 [Bun](https://bun.sh) 管理开发依赖,请勿使用 npm、pnpm 或 yarn 安装仓库依赖,以免生成额外 lockfile。
|
|
356
|
+
|
|
357
|
+
```sh
|
|
358
|
+
bun install
|
|
359
|
+
bun test # 单元测试
|
|
360
|
+
bun run test:real # 真实禅道环境集成测试
|
|
361
|
+
bun run docs:dev # 生成并预览文档站
|
|
362
|
+
bun run check # 完整 CI:测试、类型检查、注册表、构建、冒烟测试
|
|
296
363
|
```
|
|
297
364
|
|
|
298
|
-
|
|
365
|
+
`bun run test:real` 会依次读取 `.env.local` 和 `env.local`,需要配置 `ZENTAO_URL`(或 `ZENTAO_BASE_URL`),以及 `ZENTAO_TOKEN` 或 `ZENTAO_ACCOUNT` / `ZENTAO_PASSWORD`。使用 `bun run test:real -- --keep-test-data` 可保留测试创建的数据。
|
|
299
366
|
|
|
300
|
-
|
|
367
|
+
模块注册表由 `data/zentao-openapi.json` 生成。请勿手动编辑 `src/modules/generated.ts`;更新规范后运行:
|
|
301
368
|
|
|
302
369
|
```sh
|
|
303
|
-
bun run
|
|
370
|
+
bun run scripts/update-registry.ts
|
|
371
|
+
bun run docs:generate
|
|
304
372
|
```
|
|
305
373
|
|
|
306
|
-
|
|
374
|
+
提交代码前请确保 `bun run check` 通过。欢迎提交 Issue 和 Pull Request。
|
|
375
|
+
|
|
376
|
+
## License
|
|
307
377
|
|
|
308
|
-
[MIT](
|
|
378
|
+
[MIT](https://github.com/easysoft/zentao-api/blob/main/LICENSE)
|