@qingflow-tech/qingflow-app-builder-mcp 1.0.10 → 1.0.12
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 +6 -3
- package/docs/local-agent-install.md +54 -3
- package/entry_point.py +1 -1
- package/npm/bin/qingflow-skills.mjs +5 -0
- package/npm/lib/runtime.mjs +304 -13
- package/npm/scripts/postinstall.mjs +1 -5
- package/package.json +3 -2
- package/pyproject.toml +1 -1
- package/skills/qingflow-app-builder/SKILL.md +12 -12
- package/skills/qingflow-app-builder/references/create-app.md +3 -3
- package/skills/qingflow-app-builder/references/environments.md +1 -1
- package/skills/qingflow-app-builder/references/gotchas.md +1 -1
- package/skills/qingflow-app-builder/references/public-surface-sync.md +75 -0
- package/skills/qingflow-app-builder/references/tool-selection.md +6 -5
- package/skills/qingflow-app-builder/references/update-views.md +1 -1
- package/skills/qingflow-app-builder-code-integrations/SKILL.md +3 -3
- package/skills/qingflow-app-builder-code-integrations/references/code-block.md +1 -1
- package/skills/qingflow-app-builder-code-integrations/references/q-linker.md +1 -1
- package/src/qingflow_mcp/__main__.py +6 -2
- package/src/qingflow_mcp/builder_facade/models.py +41 -2
- package/src/qingflow_mcp/builder_facade/service.py +2743 -423
- package/src/qingflow_mcp/cli/commands/app.py +3 -16
- package/src/qingflow_mcp/cli/commands/builder.py +30 -4
- package/src/qingflow_mcp/cli/commands/exports.py +2 -2
- package/src/qingflow_mcp/cli/commands/imports.py +1 -1
- package/src/qingflow_mcp/cli/commands/record.py +54 -11
- package/src/qingflow_mcp/cli/context.py +0 -3
- package/src/qingflow_mcp/cli/formatters.py +238 -8
- package/src/qingflow_mcp/cli/main.py +47 -3
- package/src/qingflow_mcp/errors.py +43 -2
- package/src/qingflow_mcp/public_surface.py +24 -16
- package/src/qingflow_mcp/response_trim.py +119 -12
- package/src/qingflow_mcp/server.py +17 -14
- package/src/qingflow_mcp/server_app_builder.py +29 -7
- package/src/qingflow_mcp/server_app_user.py +23 -24
- package/src/qingflow_mcp/solution/compiler/icon_utils.py +294 -0
- package/src/qingflow_mcp/solution/executor.py +112 -15
- package/src/qingflow_mcp/tools/ai_builder_tools.py +497 -65
- package/src/qingflow_mcp/tools/app_tools.py +237 -51
- package/src/qingflow_mcp/tools/approval_tools.py +196 -34
- package/src/qingflow_mcp/tools/auth_tools.py +92 -16
- package/src/qingflow_mcp/tools/code_block_tools.py +296 -39
- package/src/qingflow_mcp/tools/custom_button_tools.py +64 -10
- package/src/qingflow_mcp/tools/directory_tools.py +236 -72
- package/src/qingflow_mcp/tools/export_tools.py +230 -33
- package/src/qingflow_mcp/tools/file_tools.py +7 -3
- package/src/qingflow_mcp/tools/import_tools.py +293 -40
- package/src/qingflow_mcp/tools/navigation_tools.py +91 -12
- package/src/qingflow_mcp/tools/package_tools.py +134 -8
- package/src/qingflow_mcp/tools/portal_tools.py +39 -3
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +116 -7
- package/src/qingflow_mcp/tools/record_tools.py +2305 -442
- package/src/qingflow_mcp/tools/resource_read_tools.py +191 -39
- package/src/qingflow_mcp/tools/role_tools.py +80 -9
- package/src/qingflow_mcp/tools/solution_tools.py +57 -15
- package/src/qingflow_mcp/tools/task_context_tools.py +569 -119
- package/src/qingflow_mcp/tools/task_tools.py +113 -29
- package/src/qingflow_mcp/tools/view_tools.py +106 -3
- package/src/qingflow_mcp/tools/workflow_tools.py +17 -1
- package/src/qingflow_mcp/tools/workspace_tools.py +71 -3
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Qingflow Core Public Surface Sync
|
|
2
|
+
|
|
3
|
+
Use this file as the maintenance baseline for the core Qingflow skills.
|
|
4
|
+
It is not a user-facing product spec. It exists to prevent skill drift.
|
|
5
|
+
|
|
6
|
+
## Current Public Defaults
|
|
7
|
+
|
|
8
|
+
### User data
|
|
9
|
+
|
|
10
|
+
- Read range first with `app_get`, then `record_browse_schema_get(view_id=...)`
|
|
11
|
+
- Treat `record_browse_schema_get.fields` as the selected Qingflow table view header schema; `record_access.fields` and `record_list` field selectors must stay aligned with it.
|
|
12
|
+
- Standard flows:
|
|
13
|
+
- analyze: `app_get -> record_browse_schema_get -> record_access -> Python`
|
|
14
|
+
- browse detail: `app_get -> record_browse_schema_get -> record_list / record_get`
|
|
15
|
+
- explicit export/download/Excel: `app_get -> choose view_id -> view_get -> record_export_*` or `record_export_direct`; export tools require explicit `view_id`
|
|
16
|
+
- insert: `record_insert_schema_get -> record_insert(items)`
|
|
17
|
+
- update: `record_get -> record_update`; use `record_update_schema_get` only for failure diagnosis or ambiguous writable-field routing
|
|
18
|
+
|
|
19
|
+
### Tasks
|
|
20
|
+
|
|
21
|
+
- Discovery stays on `task_list`
|
|
22
|
+
- `task_list --query` uses backend search first and only applies local fallback when backend returns zero rows
|
|
23
|
+
- Public actions are:
|
|
24
|
+
- `approve`
|
|
25
|
+
- `reject`
|
|
26
|
+
- `rollback`
|
|
27
|
+
- `transfer`
|
|
28
|
+
- `urge`
|
|
29
|
+
- `save_only`
|
|
30
|
+
- `reject` requires `payload.audit_feedback`
|
|
31
|
+
- `save_only` requires non-empty `fields`
|
|
32
|
+
- `TASK_RUNTIME_CONSUMED_AFTER_ACTION` is a normal post-success warning when the current node runtime is consumed and `46001` appears on re-read
|
|
33
|
+
|
|
34
|
+
### Builder
|
|
35
|
+
|
|
36
|
+
- Official package entry: `package_get`, `package_apply`
|
|
37
|
+
- Official builder writes:
|
|
38
|
+
- `app_schema_apply`
|
|
39
|
+
- `app_layout_apply`
|
|
40
|
+
- `app_flow_apply`
|
|
41
|
+
- `app_views_apply`
|
|
42
|
+
- `app_custom_buttons_apply`
|
|
43
|
+
- `app_associated_resources_apply`
|
|
44
|
+
- `app_charts_apply`
|
|
45
|
+
- `portal_apply`
|
|
46
|
+
- `app_publish_verify`
|
|
47
|
+
- `portal_apply` edit mode may omit `sections` for base-info-only updates
|
|
48
|
+
- `app_charts_apply.visibility` is a public capability and should be treated as a base-only visibility update
|
|
49
|
+
- Existing object parameter replacement should use `patch_views`, `patch_buttons`, `patch_resources`, and `patch_charts`; `upsert_*` is for creation or full target config
|
|
50
|
+
- `app_get.editability` uses:
|
|
51
|
+
- `can_edit_app_base`
|
|
52
|
+
- `can_edit_form`
|
|
53
|
+
- `can_edit_flow`
|
|
54
|
+
- `can_edit_views`
|
|
55
|
+
- `can_edit_charts`
|
|
56
|
+
|
|
57
|
+
## Known High-Drift Areas
|
|
58
|
+
|
|
59
|
+
- Task actions, especially `save_only`, reject payload requirements, and `46001` post-action interpretation
|
|
60
|
+
- Package public tools: do not regress to `package_create` / `package_attach_app` as the public default story
|
|
61
|
+
- App editability: do not let `can_edit_form` imply app base-info writes
|
|
62
|
+
- Portal and chart visibility: keep the public story on `portal_apply` / `app_charts_apply`, not low-level internal writes
|
|
63
|
+
- Analysis path: standard path stays `record_access -> Python`
|
|
64
|
+
|
|
65
|
+
## Release Checklist For Skill Maintenance
|
|
66
|
+
|
|
67
|
+
After each beta that changes public behavior, re-check:
|
|
68
|
+
|
|
69
|
+
1. `public_surface.py`
|
|
70
|
+
2. `README.md`
|
|
71
|
+
3. `server_app_builder.py` and `server.py` top-level guidance
|
|
72
|
+
4. CLI help for `task`, `builder package`, `builder portal`, `builder charts`
|
|
73
|
+
5. Whether new warnings or verification fields need to be explained in skills
|
|
74
|
+
|
|
75
|
+
If a tool behavior changed but the public surface did not, prefer updating the relevant skill section instead of expanding this file.
|
|
@@ -21,12 +21,12 @@ If the user asks for multiple forms/modules that relate to each other, this is a
|
|
|
21
21
|
|
|
22
22
|
## Resolve
|
|
23
23
|
|
|
24
|
-
- `package_get`: read one known package by `package_id`
|
|
25
|
-
- `package_apply`: create or update one package; use `create_if_missing=true` only after explicit user intent
|
|
24
|
+
- `package_get`: read one known package by `package_id`; it reads package `baseInfo` first and may warn `PACKAGE_DETAIL_READ_DEGRADED` when the richer detail endpoint needs package edit/add-app permission. Treat that warning as degraded detail, not as package-read failure.
|
|
25
|
+
- `package_apply`: create or update one package; use `create_if_missing=true` only after explicit user intent; layout/group/order changes require package edit permission because they call the backend package ordering route
|
|
26
26
|
- `member_search`: resolve named people from the directory
|
|
27
27
|
- `role_search`: resolve reusable roles from the directory
|
|
28
28
|
- `role_create`: create a reusable role when the business owner wants role-based routing
|
|
29
|
-
- `app_resolve`: locate an existing app by exactly one selector mode: `app_key`, or `app_name + package_id`
|
|
29
|
+
- `app_resolve`: locate an existing app by exactly one selector mode: `app_key`, or `app_name + package_id`; by-name resolution checks package detail/current visible apps before using broader admin-style app search, so a `/app/item` permission miss is not a frontend-visible-app failure
|
|
30
30
|
|
|
31
31
|
## Summary reads
|
|
32
32
|
|
|
@@ -36,6 +36,7 @@ If the user asks for multiple forms/modules that relate to each other, this is a
|
|
|
36
36
|
- `app_get_views`: current view names, types, columns, group-by
|
|
37
37
|
- `app_get_flow`: workflow enabled state, nodes, transitions
|
|
38
38
|
- `app_get_charts`: current chart ids, names, types, order
|
|
39
|
+
- `chart_get`: one chart's base/config detail; base info is read through the Qingflow/qflow visible route first, and config may degrade from chart data when the CHART_SEE config endpoint is unavailable. Treat degraded config as a read-mode warning, not as proof the user cannot see the chart.
|
|
39
40
|
- `portal_get`: current portal config detail and component inventory
|
|
40
41
|
|
|
41
42
|
## Apply tools
|
|
@@ -47,9 +48,9 @@ These execute normalized patches. Some app apply tools publish by default and st
|
|
|
47
48
|
- `app_flow_apply`: replace workflow
|
|
48
49
|
- `app_views_apply`: use `patch_views` for existing-view parameter replacement; use `upsert_views` for creation or full target config; remove views by key; new views default associated report/view display to visible with `limit_type="all"`
|
|
49
50
|
- `app_custom_buttons_apply`: use `patch_buttons` for existing-button parameter replacement; use `upsert_buttons` for creation or full target config; configure add-data field mappings/default values; bind buttons to header/detail/list view positions; `placement=list` maps to the backend `INSIDE` row/list button position; merge-mode view configs require `buttons`; use `view_configs[].mode="replace"` or `buttons=[]` to clear a view's custom button bindings. For child-record creation linked to the current source record, map `source_field: "数据ID"` to the target relation field.
|
|
50
|
-
- `app_associated_resources_apply`: attach existing BI reports/views to the Qingflow app associated-resource pool and per-view display area; it does not create or edit QingBI report bodies/configs. Use `patch_resources` for existing associated-resource parameter replacement; use `upsert_resources` for creation or full target config; `view_configs`, remove, and reorder may reference existing resources by internal `associated_item_id` or by `chart_id`/`chart_key`/`view_key`; use `match_mappings` for associated view/report filters; publishes after successful writes; omit raw `sourceType`, and use `report_source="dataset"` only to attach an existing BI dataset report. Before `upsert_resources`, read `app_get.associated_resources` and reuse an existing matching `target_app_key + view_key/chart_key`; repeated upsert can create duplicates because `client_key` is only valid inside one apply call.
|
|
51
|
+
- `app_associated_resources_apply`: attach existing BI reports/views to the Qingflow app associated-resource pool and per-view display area; it does not create or edit QingBI report bodies/configs. Permission is split like the backend: resource pool writes use EditAppAuth, while `view_configs` uses the view config/DataManageAuth path. Use `patch_resources` for existing associated-resource parameter replacement; use `upsert_resources` for creation or full target config; `view_configs`, remove, and reorder may reference existing resources by internal `associated_item_id` or by `chart_id`/`chart_key`/`view_key`; use `match_mappings` for associated view/report filters; publishes after successful writes; omit raw `sourceType`, and use `report_source="dataset"` only to attach an existing BI dataset report. Before `upsert_resources`, read `app_get.associated_resources` and reuse an existing matching `target_app_key + view_key/chart_key`; repeated upsert can create duplicates because `client_key` is only valid inside one apply call.
|
|
51
52
|
- `app_charts_apply`: create/edit/remove/reorder app-source QingBI report bodies/configs with `dataSourceType=qingflow`; it does not create/edit dataset BI reports and does not attach reports to Qingflow app associated-resource display. Use `patch_charts` for existing-chart parameter replacement; use `upsert_charts` for creation or full target config; supports `target/table` aliases plus QingBI chart types such as `summary`, `columnar`, `area`, `funnel`, `radar`, `scatter`, `dualaxes`, and `map`; charts are immediate-live and do not publish; use `chart_id` when names are not unique
|
|
52
|
-
- `portal_apply`: create or replace-update portal pages; use `dash_key` for update mode or `package_id + dash_name` for create mode; edit mode may omit `sections` for base-info-only updates; when sections are supplied they still use replace semantics
|
|
53
|
+
- `portal_apply`: create or replace-update portal pages; use `dash_key` for update mode or `package_id + dash_name` for create mode; create mode only prechecks package add_app, matching backend portal creation; edit mode may omit `sections` for base-info-only updates; when sections are supplied they still use replace semantics
|
|
53
54
|
|
|
54
55
|
For object-level updates, the safe partial syntax is `patch_*` with the object's real selector field plus `set` and optional `unset`. `selector` is only a concept, not a literal key. Examples: `patch_views: [{"view_key": "VIEW_KEY", "set": {...}}]`, `patch_buttons: [{"button_id": 1001, "set": {...}}]`, `patch_resources: [{"associated_item_id": 123, "set": {...}}]`, `patch_charts: [{"chart_id": 456, "set": {...}}]`. The tool reads the current backend config, merges the patch, then submits the full backend payload internally. Do not send a partial `upsert_*` and expect missing required fields to be preserved.
|
|
55
56
|
|
|
@@ -31,7 +31,7 @@ Canonical rules before any example:
|
|
|
31
31
|
- Use `filters` with canonical keys `field_name`, `operator`, `value`/`values`
|
|
32
32
|
- Use `query_conditions` for the frontend query panel. Do not put query-panel fields into `filters`.
|
|
33
33
|
- New views created by `app_views_apply` default the frontend associated report/view area to visible with `limit_type="all"`. Existing views preserve their current associated-resource display unless `associated_resources` is explicitly patched.
|
|
34
|
-
- Use `app_associated_resources_apply` for the associated report/view resource pool, selected resources, and match rules. `associated_item_id` is the internal associated-resource id from `app_get.associated_resources`; `view_configs`, remove, and reorder may also pass an existing resource's `chart_id`/`chart_key`/`view_key`, which the tool resolves to the internal id. Do not write backend raw `sourceType`; reports default to BI app reports, and dataset reports use `report_source="dataset"`. Use `match_mappings` for associated view/report filters; read `match-rules.md` if field type compatibility is unclear.
|
|
34
|
+
- Use `app_associated_resources_apply` for the associated report/view resource pool, selected resources, and match rules. Permission is split like the backend: resource pool writes use EditAppAuth, while `view_configs` uses the view config/DataManageAuth path. `associated_item_id` is the internal associated-resource id from `app_get.associated_resources`; `view_configs`, remove, and reorder may also pass an existing resource's `chart_id`/`chart_key`/`view_key`, which the tool resolves to the internal id. Do not write backend raw `sourceType`; reports default to BI app reports, and dataset reports use `report_source="dataset"`. Use `match_mappings` for associated view/report filters; read `match-rules.md` if field type compatibility is unclear.
|
|
35
35
|
- For gantt, use `start_field`, `end_field`, and optionally `title_field`
|
|
36
36
|
- If `app_get.views` or `app_get_views` shows duplicate view names, include `view_key` in `upsert_views[]` and update that exact target
|
|
37
37
|
- Builder view writes always use the raw `view_key` from `app_get.views[].view_key`, such as `emsrao25rs02`. Do not pass `custom:emsrao25rs02`; that prefixed form is only for record-data `view_id`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: qingflow-app-builder-code-integrations
|
|
3
|
-
description: Configure Qingflow code block and Q-Linker fields through the builder surface when the task involves input field insertion, alias parsing, target field binding, or troubleshooting broken code/q-linker form configurations.
|
|
3
|
+
description: Configure Qingflow code block and Q-Linker fields through the builder surface when the task involves input field insertion, alias parsing, target field binding, or troubleshooting broken code/q-linker form configurations. In Wingent Momo runtime, use the injected MCP session and recover auth/workspace only after a tool error.
|
|
4
4
|
metadata:
|
|
5
5
|
short-description: Configure Qingflow code blocks and Q-Linkers safely
|
|
6
6
|
---
|
|
@@ -58,7 +58,7 @@ For code block or Q-Linker work, use this order:
|
|
|
58
58
|
|
|
59
59
|
1. Resolve the app and read fields:
|
|
60
60
|
- `app_resolve`
|
|
61
|
-
- `
|
|
61
|
+
- `app_get_fields`
|
|
62
62
|
2. Confirm the target field set already exists.
|
|
63
63
|
3. Apply schema updates with `app_schema_apply`.
|
|
64
64
|
4. Read fields again and verify:
|
|
@@ -115,7 +115,7 @@ Hard rules:
|
|
|
115
115
|
|
|
116
116
|
After each code block or Q-Linker change, verify all of these:
|
|
117
117
|
|
|
118
|
-
- `
|
|
118
|
+
- `app_get_fields` shows the intended field type
|
|
119
119
|
- the high-level config is readable and stable
|
|
120
120
|
- target fields still have valid types
|
|
121
121
|
- insert schema can be opened
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
"""Entry point for running qingflow_mcp as a module.
|
|
2
|
-
|
|
1
|
+
"""Entry point for running qingflow_mcp as a module.
|
|
2
|
+
|
|
3
|
+
Default to the curated user-facing MCP surface. Split package and console
|
|
4
|
+
entrypoints can still start the builder server explicitly.
|
|
5
|
+
"""
|
|
6
|
+
from qingflow_mcp.server_app_user import main
|
|
3
7
|
|
|
4
8
|
if __name__ == "__main__":
|
|
5
9
|
main()
|
|
@@ -1893,8 +1893,9 @@ class PortalComponentPositionPatch(StrictModel):
|
|
|
1893
1893
|
pc_h: int = Field(default=8, validation_alias=AliasChoices("pc_h", "pcH", "h"))
|
|
1894
1894
|
mobile_x: int = Field(default=0, validation_alias=AliasChoices("mobile_x", "mobileX"))
|
|
1895
1895
|
mobile_y: int = Field(default=0, validation_alias=AliasChoices("mobile_y", "mobileY"))
|
|
1896
|
-
mobile_w: int = Field(default=
|
|
1896
|
+
mobile_w: int = Field(default=6, validation_alias=AliasChoices("mobile_w", "mobileW"))
|
|
1897
1897
|
mobile_h: int = Field(default=8, validation_alias=AliasChoices("mobile_h", "mobileH"))
|
|
1898
|
+
mobile_provided: bool = Field(default=False, exclude=True)
|
|
1898
1899
|
|
|
1899
1900
|
@model_validator(mode="before")
|
|
1900
1901
|
@classmethod
|
|
@@ -1904,6 +1905,8 @@ class PortalComponentPositionPatch(StrictModel):
|
|
|
1904
1905
|
payload = dict(value)
|
|
1905
1906
|
pc = payload.pop("pc", None)
|
|
1906
1907
|
mobile = payload.pop("mobile", None)
|
|
1908
|
+
mobile_keys = {"mobile_x", "mobileX", "mobile_y", "mobileY", "mobile_w", "mobileW", "mobile_h", "mobileH"}
|
|
1909
|
+
mobile_provided = isinstance(mobile, dict) or any(key in payload for key in mobile_keys)
|
|
1907
1910
|
if isinstance(pc, dict):
|
|
1908
1911
|
if "pc_x" not in payload and "x" in pc:
|
|
1909
1912
|
payload["pc_x"] = pc.get("x")
|
|
@@ -1922,6 +1925,7 @@ class PortalComponentPositionPatch(StrictModel):
|
|
|
1922
1925
|
payload["mobile_w"] = mobile.get("cols")
|
|
1923
1926
|
if "mobile_h" not in payload and "rows" in mobile:
|
|
1924
1927
|
payload["mobile_h"] = mobile.get("rows")
|
|
1928
|
+
payload["mobile_provided"] = mobile_provided
|
|
1925
1929
|
return payload
|
|
1926
1930
|
|
|
1927
1931
|
|
|
@@ -2003,9 +2007,10 @@ class PortalSectionPatch(StrictModel):
|
|
|
2003
2007
|
class PortalApplyRequest(StrictModel):
|
|
2004
2008
|
dash_key: str | None = None
|
|
2005
2009
|
dash_name: str | None = None
|
|
2006
|
-
package_tag_id: int | None = None
|
|
2010
|
+
package_tag_id: int | None = Field(default=None, validation_alias=AliasChoices("package_tag_id", "packageTagId", "package_id", "packageId"))
|
|
2007
2011
|
publish: bool = True
|
|
2008
2012
|
sections: list[PortalSectionPatch] = Field(default_factory=list)
|
|
2013
|
+
layout_preset: str | None = Field(default=None, validation_alias=AliasChoices("layout_preset", "layoutPreset"))
|
|
2009
2014
|
visibility: VisibilityPatch | None = None
|
|
2010
2015
|
auth: dict[str, Any] | None = None
|
|
2011
2016
|
icon: str | None = None
|
|
@@ -2014,6 +2019,33 @@ class PortalApplyRequest(StrictModel):
|
|
|
2014
2019
|
dash_global_config: dict[str, Any] | None = Field(default=None, validation_alias=AliasChoices("dash_global_config", "dashGlobalConfig"))
|
|
2015
2020
|
config: dict[str, Any] = Field(default_factory=dict)
|
|
2016
2021
|
|
|
2022
|
+
@model_validator(mode="before")
|
|
2023
|
+
@classmethod
|
|
2024
|
+
def normalize_compat_payload(cls, value: Any) -> Any:
|
|
2025
|
+
if not isinstance(value, dict):
|
|
2026
|
+
return value
|
|
2027
|
+
payload = dict(value)
|
|
2028
|
+
if "dash_name" not in payload and "dashName" not in payload and "name" in payload:
|
|
2029
|
+
payload["dash_name"] = payload.pop("name")
|
|
2030
|
+
if "sections" not in payload and "pages" in payload:
|
|
2031
|
+
pages = payload.pop("pages")
|
|
2032
|
+
if not isinstance(pages, list):
|
|
2033
|
+
raise ValueError("portal pages must be a list")
|
|
2034
|
+
if len(pages) != 1:
|
|
2035
|
+
raise ValueError("portal_apply currently supports a single page; pass one page or flatten components into sections")
|
|
2036
|
+
page = pages[0]
|
|
2037
|
+
if not isinstance(page, dict):
|
|
2038
|
+
raise ValueError("portal pages[0] must be an object")
|
|
2039
|
+
components = page.get("components")
|
|
2040
|
+
if not isinstance(components, list) or not components:
|
|
2041
|
+
raise ValueError("portal pages[0].components must be a non-empty list")
|
|
2042
|
+
payload["sections"] = components
|
|
2043
|
+
if "theme" in payload:
|
|
2044
|
+
payload.pop("theme")
|
|
2045
|
+
if "type" in payload:
|
|
2046
|
+
payload.pop("type")
|
|
2047
|
+
return payload
|
|
2048
|
+
|
|
2017
2049
|
@model_validator(mode="after")
|
|
2018
2050
|
def validate_shape(self) -> "PortalApplyRequest":
|
|
2019
2051
|
if not self.dash_key and not self.package_tag_id:
|
|
@@ -2024,6 +2056,8 @@ class PortalApplyRequest(StrictModel):
|
|
|
2024
2056
|
raise ValueError("portal apply requires a non-empty sections list when creating a portal")
|
|
2025
2057
|
if self.visibility is not None and self.auth is not None:
|
|
2026
2058
|
raise ValueError("visibility and auth cannot be provided together")
|
|
2059
|
+
if self.layout_preset is not None and self.layout_preset not in {"auto", "dashboard_2col", "dashboard_3col"}:
|
|
2060
|
+
raise ValueError("layout_preset must be one of: auto, dashboard_2col, dashboard_3col")
|
|
2027
2061
|
return self
|
|
2028
2062
|
|
|
2029
2063
|
|
|
@@ -2038,6 +2072,7 @@ class AppGetResponse(StrictModel):
|
|
|
2038
2072
|
name: str | None = None
|
|
2039
2073
|
title: str | None = None
|
|
2040
2074
|
app_icon: str | None = None
|
|
2075
|
+
icon_config: dict[str, Any] = Field(default_factory=dict)
|
|
2041
2076
|
visibility: dict[str, Any] = Field(default_factory=dict)
|
|
2042
2077
|
tag_ids: list[int] = Field(default_factory=list)
|
|
2043
2078
|
publish_status: int | None = None
|
|
@@ -2062,6 +2097,8 @@ class AppGetFieldsResponse(StrictModel):
|
|
|
2062
2097
|
app_key: str
|
|
2063
2098
|
fields: list[dict[str, Any]] = Field(default_factory=list)
|
|
2064
2099
|
field_count: int = 0
|
|
2100
|
+
chart_fields: list[dict[str, Any]] = Field(default_factory=list)
|
|
2101
|
+
chart_field_count: int = 0
|
|
2065
2102
|
form_settings: dict[str, Any] = Field(default_factory=dict)
|
|
2066
2103
|
|
|
2067
2104
|
|
|
@@ -2109,6 +2146,7 @@ class PortalReadSummaryResponse(StrictModel):
|
|
|
2109
2146
|
dash_name: str | None = None
|
|
2110
2147
|
package_tag_ids: list[int] = Field(default_factory=list)
|
|
2111
2148
|
dash_icon: str | None = None
|
|
2149
|
+
icon_config: dict[str, Any] = Field(default_factory=dict)
|
|
2112
2150
|
hide_copyright: bool | None = None
|
|
2113
2151
|
config_keys: list[str] = Field(default_factory=list)
|
|
2114
2152
|
dash_global_config_keys: list[str] = Field(default_factory=list)
|
|
@@ -2122,6 +2160,7 @@ class PortalGetResponse(StrictModel):
|
|
|
2122
2160
|
dash_name: str | None = None
|
|
2123
2161
|
package_tag_ids: list[int] = Field(default_factory=list)
|
|
2124
2162
|
dash_icon: str | None = None
|
|
2163
|
+
icon_config: dict[str, Any] = Field(default_factory=dict)
|
|
2125
2164
|
hide_copyright: bool | None = None
|
|
2126
2165
|
visibility: dict[str, Any] = Field(default_factory=dict)
|
|
2127
2166
|
auth: dict[str, Any] = Field(default_factory=dict)
|