zentao-api 0.6.8 → 0.7.0
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 +13 -0
- package/dist/browser/zentao-api.global.js +17 -17
- package/dist/modules/generated.d.ts +0 -6
- package/dist/modules/generated.js +2 -8
- package/dist/modules/override.js +97 -0
- package/dist/modules/registry-store.js +3 -0
- package/dist/request/index.d.ts +1 -0
- package/dist/request/index.js +3 -0
- package/dist/types/module.d.ts +10 -0
- package/dist/version.js +2 -2
- package/package.json +1 -1
|
@@ -507,9 +507,6 @@ export declare const BUILTIN_MODULES: ({
|
|
|
507
507
|
};
|
|
508
508
|
desc: {
|
|
509
509
|
type: string;
|
|
510
|
-
items: {
|
|
511
|
-
type: string;
|
|
512
|
-
};
|
|
513
510
|
description: string;
|
|
514
511
|
};
|
|
515
512
|
QD: {
|
|
@@ -824,9 +821,6 @@ export declare const BUILTIN_MODULES: ({
|
|
|
824
821
|
};
|
|
825
822
|
desc: {
|
|
826
823
|
type: string;
|
|
827
|
-
items: {
|
|
828
|
-
type: string;
|
|
829
|
-
};
|
|
830
824
|
description: string;
|
|
831
825
|
};
|
|
832
826
|
QD: {
|
|
@@ -582,10 +582,7 @@ export const BUILTIN_MODULES = [
|
|
|
582
582
|
"description": "评审人"
|
|
583
583
|
},
|
|
584
584
|
"desc": {
|
|
585
|
-
"type": "
|
|
586
|
-
"items": {
|
|
587
|
-
"type": "string"
|
|
588
|
-
},
|
|
585
|
+
"type": "string",
|
|
589
586
|
"description": "产品描述"
|
|
590
587
|
},
|
|
591
588
|
"QD": {
|
|
@@ -802,10 +799,7 @@ export const BUILTIN_MODULES = [
|
|
|
802
799
|
"description": "评审人"
|
|
803
800
|
},
|
|
804
801
|
"desc": {
|
|
805
|
-
"type": "
|
|
806
|
-
"items": {
|
|
807
|
-
"type": "string"
|
|
808
|
-
},
|
|
802
|
+
"type": "string",
|
|
809
803
|
"description": "产品描述"
|
|
810
804
|
},
|
|
811
805
|
"QD": {
|
package/dist/modules/override.js
CHANGED
|
@@ -2,6 +2,27 @@ import { extendModuleAction, defineModuleActions } from './define.js';
|
|
|
2
2
|
import { extractResult } from './resolve.js';
|
|
3
3
|
import { snapshotToMarkdown } from '../utils/doc-helper/markdown.js';
|
|
4
4
|
import { isRecord } from '../utils/index.js';
|
|
5
|
+
import { request } from '../request/index.js';
|
|
6
|
+
import { ZentaoError } from '../misc/errors.js';
|
|
7
|
+
/** 这些内置写接口要求 JSON,不能把 HTTP 200 的 PHP/SQL 错误文本视为成功。 */
|
|
8
|
+
const requestJSON = async ({ request: command, body, bodyType, client, timeout, insecure }) => {
|
|
9
|
+
const response = await client.request(command.path, {
|
|
10
|
+
method: command.action.method.toUpperCase(),
|
|
11
|
+
query: command.query, body, bodyType, timeout, insecure, responseType: 'response',
|
|
12
|
+
});
|
|
13
|
+
const responseText = await response.text();
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(responseText);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return {
|
|
19
|
+
status: 'fail',
|
|
20
|
+
message: `Invalid JSON response from ${command.module}/${command.action.name}.`,
|
|
21
|
+
httpStatus: response.status,
|
|
22
|
+
responseText,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
};
|
|
5
26
|
/**
|
|
6
27
|
* 内置覆盖 / 扩展定义。
|
|
7
28
|
*
|
|
@@ -64,6 +85,9 @@ import { isRecord } from '../utils/index.js';
|
|
|
64
85
|
* @internal
|
|
65
86
|
*/
|
|
66
87
|
export function applyBuiltinOverrides() {
|
|
88
|
+
for (const [moduleName, actionName] of [['product', 'create'], ['product', 'update'], ['feedback', 'close']]) {
|
|
89
|
+
extendModuleAction(moduleName, actionName, { request: requestJSON });
|
|
90
|
+
}
|
|
67
91
|
// 创建执行时,需要添加产品字段
|
|
68
92
|
extendModuleAction('execution', 'create', (action) => {
|
|
69
93
|
const required = action.requestBody.schema?.required;
|
|
@@ -92,6 +116,79 @@ export function applyBuiltinOverrides() {
|
|
|
92
116
|
}
|
|
93
117
|
return action;
|
|
94
118
|
});
|
|
119
|
+
// 需求创建和变更时,根据评审人决定是否进入评审流程。
|
|
120
|
+
['story', 'requirement', 'epic'].forEach((moduleName) => {
|
|
121
|
+
['create', 'change'].forEach((actionName) => {
|
|
122
|
+
extendModuleAction(moduleName, actionName, (action) => {
|
|
123
|
+
const schema = action.requestBody.schema;
|
|
124
|
+
// 补齐 requirement/change 的评审人字段,并允许未传评审人时执行回调。
|
|
125
|
+
schema.properties.reviewer ??= {
|
|
126
|
+
type: 'array',
|
|
127
|
+
items: { type: 'string' },
|
|
128
|
+
};
|
|
129
|
+
schema.properties.reviewer.description = '评审人员,如果无需评审则不传';
|
|
130
|
+
if (schema.required) {
|
|
131
|
+
schema.required = schema.required.filter(name => name !== 'reviewer');
|
|
132
|
+
}
|
|
133
|
+
action.beforeRequest = async ({ data = {} }) => {
|
|
134
|
+
const hasReviewer = Array.isArray(data.reviewer)
|
|
135
|
+
&& data.reviewer.some(reviewer => typeof reviewer === 'string' && reviewer.trim() !== '');
|
|
136
|
+
if (hasReviewer) {
|
|
137
|
+
data.status = 'reviewing';
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
data.needNotReview = '1';
|
|
141
|
+
}
|
|
142
|
+
return { data };
|
|
143
|
+
};
|
|
144
|
+
return action;
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
// confirmClose 是控制器参数,留在 JSON body 中会被误当成反馈表字段。
|
|
149
|
+
extendModuleAction('feedback', 'close', {
|
|
150
|
+
beforeRequest: async (command) => {
|
|
151
|
+
const data = { ...command.data };
|
|
152
|
+
const query = { ...command.query };
|
|
153
|
+
if (Object.hasOwn(data, 'confirmClose')) {
|
|
154
|
+
query.confirmClose = data.confirmClose;
|
|
155
|
+
delete data.confirmClose;
|
|
156
|
+
}
|
|
157
|
+
return { data, query };
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
// 确认表单会重置缺省字段;仅补齐可写字段,不回传详情中的其他属性。
|
|
161
|
+
extendModuleAction('bug', 'confirm', {
|
|
162
|
+
request: async ({ request: command, body, bodyType, client, timeout, insecure, options }) => {
|
|
163
|
+
const data = { ...body };
|
|
164
|
+
const fields = ['status', 'assignedTo', 'type', 'pri', 'deadline', 'mailto'];
|
|
165
|
+
const missing = fields.filter(field => data[field] === undefined);
|
|
166
|
+
if (missing.length) {
|
|
167
|
+
const response = await request('bug/get', { id: command.id }, {
|
|
168
|
+
client, timeout, insecure, throwOnFail: true,
|
|
169
|
+
forceRefreshConfig: options.forceRefreshConfig,
|
|
170
|
+
skipVersionCheckOnConfigError: options.skipVersionCheckOnConfigError,
|
|
171
|
+
});
|
|
172
|
+
const current = response.data;
|
|
173
|
+
if (!isRecord(current) || missing.some(field => current[field] === undefined)) {
|
|
174
|
+
throw new ZentaoError('E_API_FAILED', { message: 'Bug details are incomplete; confirmation was not sent.' });
|
|
175
|
+
}
|
|
176
|
+
for (const field of missing) {
|
|
177
|
+
data[field] = field === 'mailto' && typeof current[field] === 'string'
|
|
178
|
+
? current[field].split(',').filter(Boolean)
|
|
179
|
+
: current[field];
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (typeof data.status !== 'string' || !data.status.trim()) {
|
|
183
|
+
throw new ZentaoError('E_INVALID_PARAM', { param: 'status', value: String(data.status) });
|
|
184
|
+
}
|
|
185
|
+
// 已关闭 Bug 的指派人是特殊标记,API 会把它当成账号校验,需调用方明确指定。
|
|
186
|
+
if (data.assignedTo === 'closed') {
|
|
187
|
+
throw new ZentaoError('E_INVALID_PARAM', { param: 'assignedTo', value: 'closed' });
|
|
188
|
+
}
|
|
189
|
+
return requestJSON({ request: command, body: data, bodyType, client, timeout, insecure, options });
|
|
190
|
+
},
|
|
191
|
+
});
|
|
95
192
|
// 修改 acl 字段默认值为 open
|
|
96
193
|
[
|
|
97
194
|
['product', 'create'],
|
|
@@ -156,6 +156,9 @@ export function validateAction(action) {
|
|
|
156
156
|
if (action.resultType !== undefined && typeof action.resultType !== 'string') {
|
|
157
157
|
throw new ZentaoError('E_INVALID_ACTION_DEFINITION');
|
|
158
158
|
}
|
|
159
|
+
if (action.beforeRequest !== undefined && typeof action.beforeRequest !== 'function') {
|
|
160
|
+
throw new ZentaoError('E_INVALID_ACTION_DEFINITION');
|
|
161
|
+
}
|
|
159
162
|
if (action.request !== undefined && typeof action.request !== 'function') {
|
|
160
163
|
throw new ZentaoError('E_INVALID_ACTION_DEFINITION');
|
|
161
164
|
}
|
package/dist/request/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export type RequestResultFor<Name extends BuiltinRequestName> = ActionMetaOf<Nam
|
|
|
25
25
|
* 按模块名或模块动作名请求禅道 API。
|
|
26
26
|
*
|
|
27
27
|
* 选项优先级为:本次调用 options > 全局 options > 客户端默认值。
|
|
28
|
+
* 动作的 beforeRequest 在参数解析后、请求体准备前执行,返回值浅合并到解析后的请求描述中。
|
|
28
29
|
* 当响应 `status` 为 `"fail"` 时,默认按原样返回;若 `options.throwOnFail`
|
|
29
30
|
* 或全局 `throwOnFail` 为真,则改为抛出 `E_API_FAILED`。
|
|
30
31
|
* 请求前按 Action 的 `minVersion` 检查服务器版本;全局 `version` 可避免配置探测,
|
package/dist/request/index.js
CHANGED
|
@@ -231,6 +231,9 @@ async function requestInternal(name, params, options, version) {
|
|
|
231
231
|
? await autoFillUpdateParams(module, action, mergedParams, { ...options, client }, version)
|
|
232
232
|
: mergedParams;
|
|
233
233
|
const command = resolveActionRequest(module, actionName, finalParams);
|
|
234
|
+
if (command.action.beforeRequest) {
|
|
235
|
+
Object.assign(command, await command.action.beforeRequest(command));
|
|
236
|
+
}
|
|
234
237
|
const preparedBody = await prepareActionBody(command, {
|
|
235
238
|
maxUploadBytes: options.maxUploadBytes,
|
|
236
239
|
});
|
package/dist/types/module.d.ts
CHANGED
|
@@ -87,6 +87,14 @@ export type ModuleActionResultFieldMap = Readonly<Record<string, string>>;
|
|
|
87
87
|
* @param options 本次请求的数据处理选项;直接调用 getter 时可以省略。
|
|
88
88
|
*/
|
|
89
89
|
export type ModuleActionGetterFn<T, O = RequestProcessOptions> = (data: unknown, params: Record<string, unknown>, options?: O) => T;
|
|
90
|
+
/**
|
|
91
|
+
* 请求发送前的回调,在版本检查、autoFill 和参数解析完成后、请求体准备前调用。
|
|
92
|
+
* 可直接修改本次请求,或返回 `path`、`query`、`data`、`params`、`id` 的部分字段作为补丁。
|
|
93
|
+
* 返回值浅合并到解析后的请求描述中,同名字段以返回值为准;对象字段整体替换。
|
|
94
|
+
* 合并后的请求用于请求体准备、请求发送和响应处理,不会重新执行版本检查、autoFill 或参数解析。
|
|
95
|
+
* 回调抛出或拒绝时终止本次请求,错误原样传递。
|
|
96
|
+
*/
|
|
97
|
+
export type ModuleActionBeforeRequestCallback = (request: ModuleActionRequest) => Promise<Partial<Omit<ModuleActionRequest, 'module' | 'action'>>>;
|
|
90
98
|
/**
|
|
91
99
|
* 自定义动作的请求发送逻辑,在版本检查、参数解析和请求体准备完成后调用。
|
|
92
100
|
* 返回原始响应数据,后续仍按动作定义提取结果、分页并应用请求选项;抛出的错误原样传递。
|
|
@@ -149,6 +157,8 @@ export interface ModuleAction {
|
|
|
149
157
|
* 字符串为字段路径(支持 `a.b` 嵌套)、对象为字段映射、函数则接收原始响应与调用参数。
|
|
150
158
|
*/
|
|
151
159
|
resultGetter?: string | ModuleActionResultFieldMap | ModuleActionGetterFn<unknown>;
|
|
160
|
+
/** 请求体准备前执行的回调,可修改解析后的请求并返回请求描述的部分字段作为补丁。 */
|
|
161
|
+
beforeRequest?: ModuleActionBeforeRequestCallback;
|
|
152
162
|
/** 自定义请求回调;省略时通过 ZentaoClient.request 发送请求,返回值沿用相同的响应处理流程。 */
|
|
153
163
|
request?: ModuleActionRequestCallback;
|
|
154
164
|
}
|
package/dist/version.js
CHANGED