@qingflow-tech/qingflow-cli 1.1.43-beta.3 → 1.1.43-beta.5
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 +3 -3
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/skills/qingflow-app-form/SKILL.md +12 -9
- package/skills/qingflow-cli/reference/builder/30-schema-fields.md +15 -13
- package/src/qingflow_mcp/app_form_compat.py +11 -27
- package/src/qingflow_mcp/app_form_validator.py +298 -35
- package/src/qingflow_mcp/builder_facade/app_form.py +777 -150
- package/src/qingflow_mcp/builder_facade/app_form_schema.py +164 -8
- package/src/qingflow_mcp/builder_facade/schemas/app_form/manifest.json +5 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/complete.json +299 -239
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/address.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/amount.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/attachment.json +82 -30
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/code_block.json +113 -37
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/data_relation.json +87 -64
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/date.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/datetime.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/department.json +83 -28
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/email.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/link.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/long_text.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/member.json +83 -28
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/multi_select.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/number.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/phone.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/q_linker.json +95 -40
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/relation.json +100 -28
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_choice.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/single_select.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/subtable.json +201 -111
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/fields/text.json +82 -27
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/formula.json +1594 -0
- package/src/qingflow_mcp/builder_facade/schemas/app_form/v1alpha1/summary.json +143 -161
- package/src/qingflow_mcp/builder_facade/service.py +3 -2
- package/src/qingflow_mcp/cli/commands/builder.py +2 -0
- package/src/qingflow_mcp/response_trim.py +2 -0
- package/src/qingflow_mcp/server_app_builder.py +9 -4
- package/src/qingflow_mcp/tools/ai_builder_tools.py +13 -3
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Qingflow CLI 是轻流面向开发者和 AI Agent 的命令行工具。它把轻
|
|
|
7
7
|
## 安装
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install @qingflow-tech/qingflow-cli@1.1.43-beta.
|
|
10
|
+
npm install @qingflow-tech/qingflow-cli@1.1.43-beta.5
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
推荐从官网复制这一条一键安装 Qingflow CLI:
|
|
@@ -21,7 +21,7 @@ npx qingflow-cli@latest install
|
|
|
21
21
|
## 运行
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
npx -y -p @qingflow-tech/qingflow-cli@1.1.43-beta.
|
|
24
|
+
npx -y -p @qingflow-tech/qingflow-cli@1.1.43-beta.5 qingflow
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
全局安装后可以直接运行:
|
|
@@ -62,7 +62,7 @@ qingflow-skills install --agent codex --scope user
|
|
|
62
62
|
如果你通过一次性 `npx -p` 执行 skills 安装,建议加 `--copy`,避免 symlink 指向 npm 临时执行缓存:
|
|
63
63
|
|
|
64
64
|
```bash
|
|
65
|
-
npx -y -p @qingflow-tech/qingflow-cli@1.1.43-beta.
|
|
65
|
+
npx -y -p @qingflow-tech/qingflow-cli@1.1.43-beta.5 qingflow-skills install --agent codex --scope user --copy
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
`qingflow-skills install` 默认使用 symlink,且遇到同名 skill 不会覆盖;只有显式传入 `--force` 才会替换。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qingflow-tech/qingflow-cli",
|
|
3
|
-
"version": "1.1.43-beta.
|
|
3
|
+
"version": "1.1.43-beta.5",
|
|
4
4
|
"description": "Human-friendly Qingflow command line interface for auth, record operations, import, tasks, and stable builder flows.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/pyproject.toml
CHANGED
|
@@ -15,17 +15,18 @@ AppForm is one shared declaration contract. Choose one execution surface and use
|
|
|
15
15
|
| Apply | `qingflow --json builder app-form apply --file DECLARATION.json` | `app_form_apply(spec=DECLARATION)` |
|
|
16
16
|
| Delete | `qingflow --json builder app delete --app-key APP_KEY` | `app_delete(app_key=APP_KEY)` |
|
|
17
17
|
|
|
18
|
-
Before creating an app,
|
|
18
|
+
Before creating an app, let AppForm derive a stable default icon and color from `spec.appName`; do not add visual metadata to the declaration. `app_publish_verify` is retained only for an explicit final cross-resource verification; AppForm recovery never depends on it. For general surface selection, read [$qingflow-common](../qingflow-common/SKILL.md).
|
|
19
19
|
|
|
20
20
|
## Workflow
|
|
21
21
|
|
|
22
22
|
1. Read the AppForm Schema and pin its `schemaVersion` and `apiVersion` for the entire operation.
|
|
23
23
|
2. Read the pinned field Schema for every canonical field type used, including subtable child types.
|
|
24
|
-
3.
|
|
25
|
-
4.
|
|
26
|
-
5.
|
|
27
|
-
6.
|
|
28
|
-
7.
|
|
24
|
+
3. When a field uses `defaultType: 3` or another config accepts a formula, read the pinned formula Schema with CLI `--schema-kind formula` or MCP `schema_kind="formula"`; use its field-reference, operator, and function catalog instead of inventing formula syntax.
|
|
25
|
+
4. For an update, read the draft form with `being_draft=true`. Preserve every field, row, section, and subfield that should remain. For a create, build a declaration without `appKey` and with `packageId`, `spec.appName`, and complete `spec.body`; AppForm derives the icon and color from `spec.appName`.
|
|
26
|
+
5. Keep existing `queId`, `sectionId`, `attachId`, and `aliasId` values from get. Omit them only for new resources. Treat `body`, `subfields`, explicit `attachments`, and custom serial-number `components` as complete target state.
|
|
27
|
+
6. Save the declaration as JSON. In CLI, run `qingflow --json builder app-form validate --schema-version PINNED --file DECLARATION.json` before every apply and fix every reported pointer. In MCP, call apply directly; it performs the same declaration validation internally. YAML is not accepted.
|
|
28
|
+
7. Apply the declaration on the selected surface. Record a newly returned `appKey` immediately. On success, show only `operation`, the non-empty `changes/actions` summary, and warnings. On any non-success result, show the recovery diagnostics, especially deleted/changed field names in `diff/remainingDiff`, and the failed phase.
|
|
29
|
+
8. A successful apply has already read back the draft, verified the complete target, and verified the published form. Do not call get only to verify it again. For any non-success result, follow Recovery; never replay a saved backend payload or edit version.
|
|
29
30
|
|
|
30
31
|
## Read Source
|
|
31
32
|
|
|
@@ -36,7 +37,7 @@ Before creating an app, use `qingflow --json builder icon catalog` in CLI or `wo
|
|
|
36
37
|
## Identity And Deletion
|
|
37
38
|
|
|
38
39
|
- `sectionId` is a positive decimal string. `queId`, `attachId`, and `aliasId` are positive integers.
|
|
39
|
-
- A declaration with `appKey` updates
|
|
40
|
+
- A declaration with `appKey` updates. A declaration without `appKey` always creates a new app and must include `packageId` and `spec.appName`. Neither form accepts `spec.icon` or `spec.color`.
|
|
40
41
|
- Repeating a create after losing its response can create a duplicate. Once an `appKey` is known, add it to the original declaration and recover as an update.
|
|
41
42
|
- Update `packageId` only when the user wants to ensure that package references the app. It never removes other package references.
|
|
42
43
|
- Removing an existing field from `body` or a child from `subfields` deletes it. Replace a field type in two explicit applies: delete, verify, then add the new type without the old `queId`.
|
|
@@ -56,8 +57,10 @@ Before creating an app, use `qingflow --json builder icon catalog` in CLI or `wo
|
|
|
56
57
|
|
|
57
58
|
- Pin one Schema version; never combine details from different versions or silently upgrade recovery work.
|
|
58
59
|
- Ordinary and section rows contain one to four fields. A section is its own outer block. Subtable columns use only the allowed subfield types returned by the subtable detail Schema.
|
|
59
|
-
- Field declarations may set `defaultType` (`1` static, `2` relation/binding, `3` formula) and `defaultValue`;
|
|
60
|
+
- Field declarations may set `defaultType` (`1` static, `2` relation/binding, `3` formula) and `defaultValue`; `defaultValue` is `string | null` because the backend `queDefaultValue` is a string, so numeric literals must be passed as strings. For `defaultType: 3`, query the same version with `--schema-kind formula` or `schema_kind="formula"` before composing the formula. Use `null` to clear a default. These properties also apply to subtable fields. Choice defaults belong in `config.defaultOptions`; address, attachment, subtable, and relation defaults belong in `config.defaultValues`. Omit a default when none is intended.
|
|
61
|
+
- For `code_block`, when `config.codeBlockBinding.outputs` is non-empty, the `code` must explicitly assign `qf_output`, for example `qf_output = { score: input.score };`; `return {...}` alone does not satisfy output writeback, and each output `path` must point under `qf_output`.
|
|
62
|
+
- A relation formula default is `config.defaultValueFormula` and must satisfy the pinned `relation` field Schema's `FormulaExpression`; do not infer its syntax from another version or from backend DTO names.
|
|
60
63
|
- If widths are present, every field in that row must have one and the total must equal 100.
|
|
61
64
|
- Keep declarations at or below 2 MiB and 64 object/array nesting levels. For a Q-Linker `json_path` input, set `key` to the selected field's exact name so get can reconstruct the placeholder losslessly.
|
|
62
|
-
- Do not call CLI apply when the validation command fails. The command validates
|
|
65
|
+
- Do not call CLI apply when the validation command fails. The command validates the local Schema plus declaration-only cross-field rules, including resolvable integration selectors, supported output target types, and globally unique Q-Linker/code-block output targets. It is not a dry run and does not read or modify Qingflow; checks that need current or external app state remain in apply. For MCP, resolve validation errors returned by `app_form_apply` before retrying.
|
|
63
66
|
- Delete an application only after explicit user intent through the selected CLI or MCP delete operation.
|
|
@@ -34,15 +34,14 @@ The current creation shape is a complete AppForm declaration: `spec.body` contai
|
|
|
34
34
|
"packageId": 123456,
|
|
35
35
|
"spec": {
|
|
36
36
|
"appName": "生产工单",
|
|
37
|
-
"
|
|
38
|
-
"color": "green",
|
|
37
|
+
"dataTitleField": {"name": "工单编号"},
|
|
39
38
|
"body": [
|
|
40
39
|
{
|
|
41
40
|
"kind": "section",
|
|
42
41
|
"title": "基础信息",
|
|
43
42
|
"rows": [
|
|
44
43
|
{"fields": [
|
|
45
|
-
{"name": "工单编号", "type": "text"
|
|
44
|
+
{"name": "工单编号", "type": "text"},
|
|
46
45
|
{"name": "产品", "type": "text"},
|
|
47
46
|
{"name": "状态", "type": "single_select", "config": {"options": ["待排产", "生产中", "已完工"]}}
|
|
48
47
|
]},
|
|
@@ -165,9 +164,9 @@ current CLI payload.
|
|
|
165
164
|
|
|
166
165
|
## Field type reference
|
|
167
166
|
|
|
168
|
-
> **口径**:`field.
|
|
167
|
+
> **口径**:`field.questionType` 枚举和字段形状以固定版本的 **`qingflow --json builder app-form schema`** 返回结果为准;每个 canonical 类型还可用 `--field-type TYPE` 获取细节,`defaultType: 3` 的公式语法和函数目录用 `--schema-kind formula` 获取。不要从已退休的 `app_schema_apply` contract 推断当前 AppForm payload。
|
|
169
168
|
> **CLI**:表单写入使用 `qingflow [--profile NAME] --json builder app-form schema/get/validate/apply`;`validate/apply` 接受 `--file`,`get` 接受 `--app-key`。旧 `builder schema apply` 及 `builder layout apply` 不在当前 parser 的公开命令集合中。
|
|
170
|
-
> **新建应用主规则**:`spec.body`
|
|
169
|
+
> **新建应用主规则**:`spec.body` 非空时必须用 `spec.dataTitleField` 唯一选择一个顶层字段作为数据标题;数据封面可选,只能用 `spec.dataCoverField` 选择顶层 `attachment` 字段。
|
|
171
170
|
> **权限**:包内新建应用按后端 `CreateAppBean` 链路只预检目标包 **AddAppAuth**;已有应用的字段/基础信息变更才走应用自身 **EditAppAuth**。
|
|
172
171
|
|
|
173
172
|
## 0. 字段建模一眼决策表
|
|
@@ -176,7 +175,7 @@ current CLI payload.
|
|
|
176
175
|
|
|
177
176
|
| 业务语义 | 推荐写法 | 注意 |
|
|
178
177
|
| --- | --- | --- |
|
|
179
|
-
| 名称、标题、编号、短文本 | `text` |
|
|
178
|
+
| 名称、标题、编号、短文本 | `text` | 非空表单必须通过应用级 `dataTitleField` 唯一选择一个顶层字段 |
|
|
180
179
|
| 说明、备注、描述、长文本 | `long_text` | 多行文本 |
|
|
181
180
|
| 状态、类型、等级、阶段、是否枚举 | `single_select` | 必须在 `config.options` 定义非空字符串数组 |
|
|
182
181
|
| 标签、多选项、多个类别 | `multi_select` | 必须在 `config.options` 定义非空字符串数组 |
|
|
@@ -185,7 +184,7 @@ current CLI payload.
|
|
|
185
184
|
| 日期、计划日、截止日 | `date` | 同时需要时分秒才用 `datetime` |
|
|
186
185
|
| 负责人、审批人、参与人 | `member` | 不要创建“申请人/提交人”等平台系统字段 |
|
|
187
186
|
| 部门、组织 | `department` | 可按需要补 `config.departmentScope` |
|
|
188
|
-
| 图片、文件、附件 | `attachment` | 只有顶层 `attachment`
|
|
187
|
+
| 图片、文件、附件 | `attachment` | 只有顶层 `attachment` 可被应用级 `dataCoverField` 选择 |
|
|
189
188
|
| 跨应用业务对象关系 | `relation` | 使用已确认的 `targetAppKey`;`displayField` / `visibleFields` 必须写对象选择器 |
|
|
190
189
|
|
|
191
190
|
禁止创建平台系统字段:`数据ID`、`编号`、`申请人`、`申请时间`、`创建人`、`创建时间`、`提交人`、`提交时间`、`更新时间`、`更新人`、`当前流程状态`、`当前处理人`、`当前处理节点`、`流程标题`。需要引用系统字段时,只在工具明确支持的位置引用,例如按钮 `source_field: "数据ID"`。
|
|
@@ -196,7 +195,7 @@ current CLI payload.
|
|
|
196
195
|
|
|
197
196
|
固定 AppForm Schema 中可用的字段类型为:
|
|
198
197
|
|
|
199
|
-
| `
|
|
198
|
+
| `questionType` | 说明(搭建语义) |
|
|
200
199
|
|--------|------------------|
|
|
201
200
|
| `text` | 单行文本 |
|
|
202
201
|
| `long_text` | 多行文本 |
|
|
@@ -238,14 +237,14 @@ current CLI payload.
|
|
|
238
237
|
|
|
239
238
|
## 2. 按类型的必填 / 常用可选(写意图)
|
|
240
239
|
|
|
241
|
-
| `
|
|
240
|
+
| `questionType` | 通常必填 | 常见可选 / 约束 |
|
|
242
241
|
|--------|----------|-----------------|
|
|
243
|
-
| 标量与选择器 | `name` + `
|
|
242
|
+
| 标量与选择器 | `name` + `questionType`(+ 业务 `beingRequired` 等) | `questionDescription`、`beingRequired`;数据标题和封面在应用级 `dataTitleField/dataCoverField` 中选择;**`single_select` / `multi_select` 的 `config.options` 是非空字符串数组**,如 `["A", "B"]` |
|
|
244
243
|
| `department` | 同左 | **`config.departmentScope`**:`mode: all \| custom`;`custom` 时 **`departments[]`**(含 `deptId` 等) |
|
|
245
244
|
| `relation` | **`targetAppKey`**;另需 **`displayField`**、**`visibleFields[]`** | **`relationMode`**;目标应用元数据不可读时先读已发布表单,不要猜目标 key |
|
|
246
245
|
| `subtable` | **`subfields[]` 非空** | 子列递归同父级规则;使用 Schema 返回的 camelCase 子字段配置 |
|
|
247
246
|
| `q_linker` | **`config.qLinkerBinding`**:`inputs`、`request`、**`outputs[]` 每项须含 `targetField`**(能解析到本表单字段) | `request.url` 等须符合**租户/后端远程查询策略**;以字段类型 Schema 返回的配置为准 |
|
|
248
|
-
| `code_block` | **`config.codeBlockBinding`**:`inputs`、`code`、**`outputs[]` + `targetField
|
|
247
|
+
| `code_block` | **`config.codeBlockBinding`**:`inputs`、`code`、**`outputs[]` + `targetField`**;配置 outputs 时,代码必须显式写 **`qf_output = {...}`**,仅 `return {...}` 不会产生可回写结果,且每个 `outputs[].path` 必须从 `qf_output` 取值 | 以字段类型 Schema 返回的 camelCase 配置为准 |
|
|
249
248
|
|
|
250
249
|
别名:契约会把 `title`/`label` 映射到 **`name`** 等;字段类型自然别名见 §1.1。
|
|
251
250
|
|
|
@@ -268,6 +267,8 @@ current CLI payload.
|
|
|
268
267
|
|
|
269
268
|
只在目标应用已经创建并通过 readback 确认后填写 `targetAppKey`。如果返回选择器校验错误,修复 `config.displayField` / `config.visibleFields`,不要重建应用或改 `appKey`。
|
|
270
269
|
|
|
270
|
+
引用字段的公式默认值写在 `config.defaultValueFormula`,表达式必须符合固定版本 `relation` 字段详情中的 `FormulaExpression`。不要把后端 `referenceConfig` 字段名或其他 Schema 版本的公式写法直接放进声明。
|
|
271
|
+
|
|
271
272
|
### 2.2 工作流状态字段
|
|
272
273
|
|
|
273
274
|
要启用工作流的应用,字段阶段就创建一个明确的业务状态单选字段,例如 `状态`、`处理状态`、`审批状态`、`工单状态`、`计划状态`、`报工状态`、`单据状态`。领域结果字段不等价于流程状态;例如质量场景的 `检验结论` 只能表达结果,仍建议另建 `处理状态` 用于流程节点、视图和报表。
|
|
@@ -279,7 +280,7 @@ current CLI payload.
|
|
|
279
280
|
| 场景 | 当前入口 | 说明 |
|
|
280
281
|
|------|----------------|------|
|
|
281
282
|
| 编辑已有应用 | `qingflow --json builder app-form get --app-key APP_KEY --being-draft` -> `qingflow --json builder app-form validate --schema-version VERSION --file DECLARATION.json` -> `qingflow --json builder app-form apply --file DECLARATION.json` | 保留完整 `body` 和现有 IDs;不要提交部分字段 patch |
|
|
282
|
-
| 包内新建应用 | `qingflow --json builder app-form schema` -> declaration without `appKey` -> `qingflow --json builder app-form validate --schema-version VERSION --file DECLARATION.json` -> `qingflow --json builder app-form apply --file DECLARATION.json` | 须含 `packageId`、`spec.appName
|
|
283
|
+
| 包内新建应用 | `qingflow --json builder app-form schema` -> declaration without `appKey` -> `qingflow --json builder app-form validate --schema-version VERSION --file DECLARATION.json` -> `qingflow --json builder app-form apply --file DECLARATION.json` | 须含 `packageId`、`spec.appName` 和完整 `body`;图标与颜色按应用名生成 |
|
|
283
284
|
| 多应用系统 | 每个 app 独立走 AppForm;先创建无关系 app,再用已确认 `appKey` 更新关系 | 不使用旧 `apps-file` 批量 schema 路径 |
|
|
284
285
|
| 删除应用 | 显式用户意图 -> `qingflow --json builder app delete --app-key APP_KEY` | 不通过字段删除 |
|
|
285
286
|
|
|
@@ -291,7 +292,8 @@ current CLI payload.
|
|
|
291
292
|
|------|------|
|
|
292
293
|
| **当前完整系统主入口** | [20-build-complete-system.md](./20-build-complete-system.md) |
|
|
293
294
|
| **历史字段类型能力探针(不是新建主模板)** | [schema_apply_add_fields_all_types.json](../examples/schema/schema_apply_add_fields_all_types.json)(仅用于旧字段能力覆盖测试;当前新建和更新统一使用上文 AppForm) |
|
|
294
|
-
| **`q_linker` / `code_block
|
|
295
|
+
| **`q_linker` / `code_block` / relation 配置**(契约内置形状) | `qingflow --json builder app-form schema --schema-version VERSION --field-type TYPE` 返回的固定版本字段详情 |
|
|
296
|
+
| **默认值与其他公式文本** | `qingflow --json builder app-form schema --schema-version VERSION --schema-kind formula` 返回的同版本公式语法、字段引用、运算符和函数目录 |
|
|
295
297
|
| 最小新建字段(1 个 text 标题字段) | [schema_add_fields_minimal.example.json](../examples/schema/schema_add_fields_minimal.example.json) |
|
|
296
298
|
| 历史 scalar / complex 批测(可选) | [_batch_schema_scalar.json](../examples/schema/_batch_schema_scalar.json)、[_batch_schema_complex.json](../examples/schema/_batch_schema_complex.json) |
|
|
297
299
|
|
|
@@ -5,7 +5,6 @@ from typing import Any
|
|
|
5
5
|
|
|
6
6
|
from .builder_facade.app_form_schema import AppFormContractError
|
|
7
7
|
from .json_types import JSONObject
|
|
8
|
-
from .solution.compiler.icon_utils import validate_workspace_icon_choice
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
TYPE_ALIASES = {
|
|
@@ -40,18 +39,25 @@ def normalize_app_form_declaration(payload: JSONObject) -> tuple[JSONObject, lis
|
|
|
40
39
|
warnings: list[JSONObject] = []
|
|
41
40
|
|
|
42
41
|
def visit(field: JSONObject, path: str) -> None:
|
|
43
|
-
raw_type = str(field.get("type") or "").strip()
|
|
42
|
+
raw_type = str(field.get("questionType") or field.get("type") or "").strip()
|
|
44
43
|
canonical_type = TYPE_ALIASES.get(raw_type, raw_type)
|
|
45
44
|
if canonical_type:
|
|
46
|
-
field["
|
|
45
|
+
field["questionType"] = canonical_type
|
|
46
|
+
if "type" in field:
|
|
47
|
+
if "questionType" in field and str(field.get("type") or "").strip() != raw_type:
|
|
48
|
+
raise AppFormContractError("INVALID_SPEC", "conflicting aliases: type and questionType", path=path)
|
|
49
|
+
field.pop("type", None)
|
|
47
50
|
if canonical_type != raw_type and raw_type == "boolean":
|
|
48
51
|
warnings.append({"code": "LEGACY_BOOLEAN_NORMALIZED", "path": f"{path}/type"})
|
|
49
52
|
|
|
50
53
|
for old, new in (
|
|
51
|
-
("
|
|
52
|
-
("
|
|
54
|
+
("required", "beingRequired"),
|
|
55
|
+
("description", "questionDescription"),
|
|
53
56
|
("default_type", "defaultType"),
|
|
54
57
|
("default_value", "defaultValue"),
|
|
58
|
+
("question_description", "questionDescription"),
|
|
59
|
+
("question_type", "questionType"),
|
|
60
|
+
("being_required", "beingRequired"),
|
|
55
61
|
):
|
|
56
62
|
if old not in field:
|
|
57
63
|
continue
|
|
@@ -96,25 +102,3 @@ def normalize_app_form_declaration(payload: JSONObject) -> tuple[JSONObject, lis
|
|
|
96
102
|
if isinstance(field, dict):
|
|
97
103
|
visit(field, f"/spec/body/{block_index}/rows/{row_index}/fields/{field_index}")
|
|
98
104
|
return result, warnings
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def validate_app_form_create_icon(payload: JSONObject) -> None:
|
|
102
|
-
if "appKey" in payload:
|
|
103
|
-
return
|
|
104
|
-
spec = payload.get("spec")
|
|
105
|
-
if not isinstance(spec, dict):
|
|
106
|
-
return
|
|
107
|
-
ok, error_code, message, details = validate_workspace_icon_choice(
|
|
108
|
-
icon=str(spec.get("icon") or "") or None,
|
|
109
|
-
color=str(spec.get("color") or "") or None,
|
|
110
|
-
require_explicit=True,
|
|
111
|
-
disallow_generic=True,
|
|
112
|
-
)
|
|
113
|
-
if not ok:
|
|
114
|
-
color_errors = {"WORKSPACE_ICON_COLOR_REQUIRED", "WORKSPACE_ICON_COLOR_NOT_FOUND"}
|
|
115
|
-
raise AppFormContractError(
|
|
116
|
-
error_code or "WORKSPACE_ICON_INVALID",
|
|
117
|
-
message or "invalid workspace icon configuration",
|
|
118
|
-
path="/spec/color" if error_code in color_errors else "/spec/icon",
|
|
119
|
-
details=details,
|
|
120
|
-
)
|