@josephyan/qingflow-cli 1.0.11 → 1.1.2

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.
Files changed (67) hide show
  1. package/README.md +3 -3
  2. package/npm/bin/qingflow.mjs +40 -2
  3. package/npm/lib/runtime.mjs +386 -15
  4. package/npm/scripts/postinstall.mjs +7 -2
  5. package/package.json +1 -1
  6. package/pyproject.toml +1 -1
  7. package/skills/qingflow-cli/SKILL.md +440 -0
  8. package/skills/qingflow-cli/manifest.yaml +10 -0
  9. package/skills/qingflow-cli/reference/QINGFLOW_CLI_ADMIN_CHEATSHEET.md +94 -0
  10. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md +485 -0
  11. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_CHARTS_WORKFLOW.md +237 -0
  12. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_MATCH_RULES.md +137 -0
  13. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_PORTAL_WORKFLOW.md +263 -0
  14. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md +304 -0
  15. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_WORKSPACE_ICONS.md +41 -0
  16. package/skills/qingflow-cli/reference/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md +139 -0
  17. package/skills/qingflow-cli/reference/QINGFLOW_CLI_EXPLORATION_REPORT.md +84 -0
  18. package/skills/qingflow-cli/reference/QINGFLOW_CLI_FIELD_DATA_TYPES.md +129 -0
  19. package/skills/qingflow-cli/reference/QINGFLOW_CLI_MEMBER_CHEATSHEET.md +195 -0
  20. package/skills/qingflow-cli/reference/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md +159 -0
  21. package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md +20 -0
  22. package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md +176 -0
  23. package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md +163 -0
  24. package/skills/qingflow-cli/reference/QINGFLOW_CLI_SCHEMA_APPLY_FIELD_TYPES_AND_SCENARIOS.md +107 -0
  25. package/skills/qingflow-cli/reference/QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md +151 -0
  26. package/skills/qingflow-cli/reference/_batch_schema_complex.json +18 -0
  27. package/skills/qingflow-cli/reference/_batch_schema_scalar.json +17 -0
  28. package/skills/qingflow-cli/reference/charts_remove.example.json +1 -0
  29. package/skills/qingflow-cli/reference/charts_reorder.example.json +1 -0
  30. package/skills/qingflow-cli/reference/charts_upsert_bar.example.json +8 -0
  31. package/skills/qingflow-cli/reference/charts_upsert_dashboard_starter.example.json +37 -0
  32. package/skills/qingflow-cli/reference/charts_upsert_minimal.example.json +13 -0
  33. package/skills/qingflow-cli/reference/portal_sections_all_types.example.json +131 -0
  34. package/skills/qingflow-cli/reference/portal_sections_five_types.example.json +126 -0
  35. package/skills/qingflow-cli/reference/portal_sections_standard_workbench.example.json +128 -0
  36. package/skills/qingflow-cli/reference/schema_add_fields_minimal.example.json +7 -0
  37. package/skills/qingflow-cli/reference/schema_apply_add_fields_all_types.json +78 -0
  38. package/skills/qingflow-cli/reference/views_upsert_table_minimal.example.json +7 -0
  39. package/skills/qingflow-cli/scripts/builder-package-from-app-list.py +140 -0
  40. package/skills/qingflow-cli/scripts/find-app-by-keyword.py +132 -0
  41. package/skills/qingflow-cli/scripts/validate_qingflow_output_files.py +87 -0
  42. package/src/qingflow_mcp/__init__.py +1 -1
  43. package/src/qingflow_mcp/builder_facade/models.py +532 -48
  44. package/src/qingflow_mcp/builder_facade/service.py +9194 -2384
  45. package/src/qingflow_mcp/builder_facade/workflow_spec.py +111 -0
  46. package/src/qingflow_mcp/cli/commands/app.py +3 -16
  47. package/src/qingflow_mcp/cli/commands/builder.py +354 -56
  48. package/src/qingflow_mcp/cli/commands/record.py +89 -2
  49. package/src/qingflow_mcp/cli/formatters.py +32 -1
  50. package/src/qingflow_mcp/cli/main.py +245 -3
  51. package/src/qingflow_mcp/public_surface.py +11 -8
  52. package/src/qingflow_mcp/response_trim.py +143 -14
  53. package/src/qingflow_mcp/server.py +15 -12
  54. package/src/qingflow_mcp/server_app_builder.py +108 -30
  55. package/src/qingflow_mcp/server_app_user.py +17 -18
  56. package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
  57. package/src/qingflow_mcp/solution/compiler/icon_utils.py +294 -0
  58. package/src/qingflow_mcp/solution/executor.py +3 -133
  59. package/src/qingflow_mcp/tools/ai_builder_tools.py +2617 -440
  60. package/src/qingflow_mcp/tools/app_tools.py +53 -8
  61. package/src/qingflow_mcp/tools/package_tools.py +16 -2
  62. package/src/qingflow_mcp/tools/record_tools.py +2095 -176
  63. package/src/qingflow_mcp/tools/resource_read_tools.py +3 -0
  64. package/src/qingflow_mcp/tools/solution_tools.py +30 -2
  65. package/src/qingflow_mcp/tools/workflow_tools.py +3 -31
  66. package/src/qingflow_mcp/version.py +110 -0
  67. package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +0 -173
@@ -9,6 +9,22 @@ from .button_style_catalog import resolve_button_style
9
9
  from ..solution.spec_models import StrictModel
10
10
 
11
11
 
12
+ def _normalize_builder_view_key_value(value: Any) -> Any:
13
+ if not isinstance(value, str):
14
+ return value
15
+ raw = value.strip()
16
+ if raw.startswith("custom:"):
17
+ return raw.split(":", 1)[1].strip()
18
+ return raw
19
+
20
+
21
+ def _normalize_builder_view_key_payload(payload: dict[str, Any]) -> dict[str, Any]:
22
+ for key in ("view_key", "viewKey", "viewgraphKey", "viewGraphKey"):
23
+ if key in payload:
24
+ payload[key] = _normalize_builder_view_key_value(payload[key])
25
+ return payload
26
+
27
+
12
28
  class PublicFieldType(str, Enum):
13
29
  text = "text"
14
30
  long_text = "long_text"
@@ -115,14 +131,45 @@ class PublicViewButtonType(str, Enum):
115
131
  class PublicViewButtonConfigType(str, Enum):
116
132
  top = "TOP"
117
133
  detail = "DETAIL"
134
+ list = "INSIDE"
135
+
136
+
137
+ class PublicButtonPlacement(str, Enum):
138
+ header = "header"
139
+ detail = "detail"
140
+ list = "list"
118
141
 
119
142
 
120
143
  class PublicChartType(str, Enum):
121
144
  target = "target"
145
+ indicator = "indicator"
146
+ summary = "summary"
122
147
  pie = "pie"
123
148
  bar = "bar"
149
+ columnar = "columnar"
124
150
  line = "line"
125
151
  table = "table"
152
+ detail = "detail"
153
+ area = "area"
154
+ stacked_area = "stacked_area"
155
+ pct_stack_area = "pct_stack_area"
156
+ funnel = "funnel"
157
+ waterfall = "waterfall"
158
+ gauge = "gauge"
159
+ heatmap = "heatmap"
160
+ histogram = "histogram"
161
+ treemap = "treemap"
162
+ radar = "radar"
163
+ stacked_bar = "stacked_bar"
164
+ pct_stack_bar = "pct_stack_bar"
165
+ stacked_column = "stacked_column"
166
+ pct_stack_col = "pct_stack_col"
167
+ scatter = "scatter"
168
+ ring = "ring"
169
+ rose = "rose"
170
+ dualaxes = "dualaxes"
171
+ map = "map"
172
+ timeline = "timeline"
126
173
 
127
174
 
128
175
  class LayoutApplyMode(str, Enum):
@@ -136,10 +183,6 @@ class LayoutPreset(str, Enum):
136
183
  single_section = "single_section"
137
184
 
138
185
 
139
- class FlowPreset(str, Enum):
140
- basic_approval = "basic_approval"
141
- basic_fill_then_approve = "basic_fill_then_approve"
142
-
143
186
 
144
187
  class ViewsPreset(str, Enum):
145
188
  default_table = "default_table"
@@ -739,6 +782,8 @@ class FieldPatch(StrictModel):
739
782
  default=None,
740
783
  validation_alias=AliasChoices("custom_button_text", "customButtonText", "custom_btn_text", "customBtnText"),
741
784
  )
785
+ as_data_title: bool | None = Field(default=None, validation_alias=AliasChoices("as_data_title", "asDataTitle"))
786
+ as_data_cover: bool | None = Field(default=None, validation_alias=AliasChoices("as_data_cover", "asDataCover"))
742
787
  subfields: list["FieldPatch"] = Field(default_factory=list)
743
788
 
744
789
  @model_validator(mode="after")
@@ -819,6 +864,8 @@ class FieldMutation(StrictModel):
819
864
  default=None,
820
865
  validation_alias=AliasChoices("custom_button_text", "customButtonText", "custom_btn_text", "customBtnText"),
821
866
  )
867
+ as_data_title: bool | None = Field(default=None, validation_alias=AliasChoices("as_data_title", "asDataTitle"))
868
+ as_data_cover: bool | None = Field(default=None, validation_alias=AliasChoices("as_data_cover", "asDataCover"))
822
869
  subfields: list[FieldPatch] | None = None
823
870
  subfield_updates: list["FieldUpdatePatch"] | None = Field(
824
871
  default=None,
@@ -1013,6 +1060,27 @@ class FlowTransitionPatch(StrictModel):
1013
1060
  target: str = Field(alias="to")
1014
1061
 
1015
1062
 
1063
+ class ViewQueryConditionsPatch(StrictModel):
1064
+ enabled: bool = Field(default=True, validation_alias=AliasChoices("enabled", "queryConditionStatus", "status"))
1065
+ exact: bool = Field(default=False, validation_alias=AliasChoices("exact", "queryConditionExact"))
1066
+ hide_before_query: bool = Field(default=False, validation_alias=AliasChoices("hide_before_query", "hideBeforeQuery", "hideBeforeQueryCondition"))
1067
+ rows: list[list[str | int]] = Field(default_factory=list, validation_alias=AliasChoices("rows", "fields", "queryCondition"))
1068
+
1069
+
1070
+ class ViewAssociatedResourcesPatch(StrictModel):
1071
+ visible: bool = Field(default=True, validation_alias=AliasChoices("visible", "enabled", "asosChartVisible"))
1072
+ limit_type: str | None = Field(default=None, validation_alias=AliasChoices("limit_type", "limitType"))
1073
+ associated_item_ids: list[Any] = Field(
1074
+ default_factory=list,
1075
+ validation_alias=AliasChoices(
1076
+ "associated_item_ids",
1077
+ "associatedItemIds",
1078
+ "asosChartIdList",
1079
+ "items",
1080
+ ),
1081
+ )
1082
+
1083
+
1016
1084
  class ViewUpsertPatch(StrictModel):
1017
1085
  name: str
1018
1086
  view_key: str | None = Field(default=None, validation_alias=AliasChoices("view_key", "viewKey"))
@@ -1025,6 +1093,22 @@ class ViewUpsertPatch(StrictModel):
1025
1093
  title_field: str | None = Field(default=None, validation_alias=AliasChoices("title_field", "titleField"))
1026
1094
  buttons: list["ViewButtonBindingPatch"] | None = None
1027
1095
  visibility: VisibilityPatch | None = None
1096
+ query_conditions: ViewQueryConditionsPatch | None = Field(default=None, validation_alias=AliasChoices("query_conditions", "queryConditions", "query_condition", "queryCondition"))
1097
+ associated_resources: ViewAssociatedResourcesPatch | None = Field(
1098
+ default=None,
1099
+ validation_alias=AliasChoices(
1100
+ "associated_resources",
1101
+ "associatedResources",
1102
+ "associated_reports",
1103
+ "associatedReports",
1104
+ "asosChartConfig",
1105
+ ),
1106
+ )
1107
+ partial_update: bool = Field(default=False, exclude=True, validation_alias=AliasChoices("_partial_update", "partial_update", "partialUpdate"))
1108
+ preserve_filters: bool = Field(default=False, exclude=True, validation_alias=AliasChoices("_preserve_filters", "preserve_filters", "preserveFilters"))
1109
+ preserve_buttons: bool = Field(default=False, exclude=True, validation_alias=AliasChoices("_preserve_buttons", "preserve_buttons", "preserveButtons"))
1110
+ preserve_query_conditions: bool = Field(default=False, exclude=True, validation_alias=AliasChoices("_preserve_query_conditions", "preserve_query_conditions", "preserveQueryConditions"))
1111
+ preserve_associated_resources: bool = Field(default=False, exclude=True, validation_alias=AliasChoices("_preserve_associated_resources", "preserve_associated_resources", "preserveAssociatedResources"))
1028
1112
 
1029
1113
  @model_validator(mode="before")
1030
1114
  @classmethod
@@ -1032,6 +1116,7 @@ class ViewUpsertPatch(StrictModel):
1032
1116
  if not isinstance(value, dict):
1033
1117
  return value
1034
1118
  payload = dict(value)
1119
+ payload = _normalize_builder_view_key_payload(payload)
1035
1120
  if "fields" in payload and "columns" not in payload:
1036
1121
  payload["columns"] = payload.pop("fields")
1037
1122
  if "column_names" in payload and "columns" not in payload:
@@ -1042,6 +1127,20 @@ class ViewUpsertPatch(StrictModel):
1042
1127
  payload["filters"] = payload.pop("filter_rules")
1043
1128
  if "filterRules" in payload and "filters" not in payload:
1044
1129
  payload["filters"] = payload.pop("filterRules")
1130
+ if "queryConditions" in payload and "query_conditions" not in payload:
1131
+ payload["query_conditions"] = payload.pop("queryConditions")
1132
+ if "query_condition" in payload and "query_conditions" not in payload:
1133
+ payload["query_conditions"] = payload.pop("query_condition")
1134
+ if "queryCondition" in payload and "query_conditions" not in payload:
1135
+ payload["query_conditions"] = payload.pop("queryCondition")
1136
+ if "associatedResources" in payload and "associated_resources" not in payload:
1137
+ payload["associated_resources"] = payload.pop("associatedResources")
1138
+ if "associated_reports" in payload and "associated_resources" not in payload:
1139
+ payload["associated_resources"] = payload.pop("associated_reports")
1140
+ if "associatedReports" in payload and "associated_resources" not in payload:
1141
+ payload["associated_resources"] = payload.pop("associatedReports")
1142
+ if "asosChartConfig" in payload and "associated_resources" not in payload:
1143
+ payload["associated_resources"] = payload.pop("asosChartConfig")
1045
1144
  raw_type = payload.get("type")
1046
1145
  if isinstance(raw_type, str):
1047
1146
  normalized = raw_type.strip().lower()
@@ -1066,6 +1165,28 @@ class ViewUpsertPatch(StrictModel):
1066
1165
  return self
1067
1166
 
1068
1167
 
1168
+ class ViewPartialPatch(StrictModel):
1169
+ view_key: str | None = Field(default=None, validation_alias=AliasChoices("view_key", "viewKey", "viewgraphKey", "viewGraphKey"))
1170
+ name: str | None = Field(default=None, validation_alias=AliasChoices("name", "view_name", "viewName"))
1171
+ set: dict[str, Any] = Field(default_factory=dict, validation_alias=AliasChoices("set", "update", "values"))
1172
+ unset: list[str] = Field(default_factory=list, validation_alias=AliasChoices("unset", "clear", "remove"))
1173
+
1174
+ @model_validator(mode="before")
1175
+ @classmethod
1176
+ def normalize_aliases(cls, value: Any) -> Any:
1177
+ if not isinstance(value, dict):
1178
+ return value
1179
+ return _normalize_builder_view_key_payload(dict(value))
1180
+
1181
+ @model_validator(mode="after")
1182
+ def validate_shape(self) -> "ViewPartialPatch":
1183
+ if not str(self.view_key or "").strip() and not str(self.name or "").strip():
1184
+ raise ValueError("patch_views[] requires view_key or unique name")
1185
+ if not self.set and not self.unset:
1186
+ raise ValueError("patch_views[] requires set or unset")
1187
+ return self
1188
+
1189
+
1069
1190
  class CustomButtonJudgeValuePatch(StrictModel):
1070
1191
  id: int | str | None = None
1071
1192
  value: Any | None = None
@@ -1128,13 +1249,44 @@ class CustomButtonMatchRulePatch(StrictModel):
1128
1249
  return payload
1129
1250
 
1130
1251
 
1252
+ class CustomButtonFieldMappingPatch(StrictModel):
1253
+ source_field: Any = Field(validation_alias=AliasChoices("source_field", "sourceField", "source"))
1254
+ target_field: Any = Field(validation_alias=AliasChoices("target_field", "targetField", "target"))
1255
+
1256
+
1257
+ class FieldMatchMappingPatch(StrictModel):
1258
+ target_field: Any = Field(validation_alias=AliasChoices("target_field", "targetField", "target", "field"))
1259
+ source_field: Any | None = Field(default=None, validation_alias=AliasChoices("source_field", "sourceField", "source"))
1260
+ value: Any | None = Field(default=None, validation_alias=AliasChoices("value", "static_value", "staticValue"))
1261
+ operator: str = Field(default="eq", validation_alias=AliasChoices("operator", "op", "judge_type", "judgeType"))
1262
+
1263
+ @model_validator(mode="after")
1264
+ def validate_shape(self) -> "FieldMatchMappingPatch":
1265
+ has_source = self.source_field is not None and str(self.source_field).strip() != ""
1266
+ has_value = self.value is not None
1267
+ if has_source == has_value:
1268
+ raise ValueError("match_mappings[] requires exactly one of source_field or value")
1269
+ return self
1270
+
1271
+
1131
1272
  class CustomButtonAddDataConfigPatch(StrictModel):
1132
- related_app_key: str | None = Field(default=None, validation_alias=AliasChoices("related_app_key", "relatedAppKey"))
1273
+ related_app_key: str | None = Field(
1274
+ default=None,
1275
+ validation_alias=AliasChoices("related_app_key", "relatedAppKey", "target_app_key", "targetAppKey"),
1276
+ )
1133
1277
  related_app_name: str | None = Field(default=None, validation_alias=AliasChoices("related_app_name", "relatedAppName"))
1134
1278
  que_relation: list[CustomButtonMatchRulePatch] = Field(
1135
1279
  default_factory=list,
1136
1280
  validation_alias=AliasChoices("que_relation", "queRelation"),
1137
1281
  )
1282
+ field_mappings: list[CustomButtonFieldMappingPatch] = Field(
1283
+ default_factory=list,
1284
+ validation_alias=AliasChoices("field_mappings", "fieldMappings", "mappings"),
1285
+ )
1286
+ default_values: dict[str, Any] = Field(
1287
+ default_factory=dict,
1288
+ validation_alias=AliasChoices("default_values", "defaultValues", "defaults"),
1289
+ )
1138
1290
 
1139
1291
 
1140
1292
  class CustomButtonExternalQRobotConfigPatch(StrictModel):
@@ -1241,6 +1393,240 @@ class CustomButtonPatch(StrictModel):
1241
1393
  return self
1242
1394
 
1243
1395
 
1396
+ class CustomButtonUpsertPatch(CustomButtonPatch):
1397
+ button_id: int | None = Field(default=None, validation_alias=AliasChoices("button_id", "buttonId", "id"))
1398
+ client_key: str | None = Field(default=None, validation_alias=AliasChoices("client_key", "clientKey"))
1399
+
1400
+
1401
+ class CustomButtonPartialPatch(StrictModel):
1402
+ button_id: int | None = Field(default=None, validation_alias=AliasChoices("button_id", "buttonId", "id"))
1403
+ button_text: str | None = Field(default=None, validation_alias=AliasChoices("button_text", "buttonText", "name"))
1404
+ client_key: str | None = Field(default=None, validation_alias=AliasChoices("client_key", "clientKey"))
1405
+ set: dict[str, Any] = Field(default_factory=dict, validation_alias=AliasChoices("set", "update", "values"))
1406
+ unset: list[str] = Field(default_factory=list, validation_alias=AliasChoices("unset", "clear", "remove"))
1407
+
1408
+ @model_validator(mode="after")
1409
+ def validate_shape(self) -> "CustomButtonPartialPatch":
1410
+ if self.button_id is None and not str(self.button_text or "").strip():
1411
+ raise ValueError("patch_buttons[] requires button_id or unique button_text")
1412
+ if not self.set and not self.unset:
1413
+ raise ValueError("patch_buttons[] requires set or unset")
1414
+ return self
1415
+
1416
+
1417
+ class CustomButtonRemovePatch(StrictModel):
1418
+ button_id: int | None = Field(default=None, validation_alias=AliasChoices("button_id", "buttonId", "id"))
1419
+ button_text: str | None = Field(default=None, validation_alias=AliasChoices("button_text", "buttonText", "name"))
1420
+
1421
+ @model_validator(mode="after")
1422
+ def validate_selector(self) -> "CustomButtonRemovePatch":
1423
+ if self.button_id is None and not str(self.button_text or "").strip():
1424
+ raise ValueError("remove_buttons[] requires button_id or button_text")
1425
+ return self
1426
+
1427
+
1428
+ class CustomButtonViewButtonBindingPatch(StrictModel):
1429
+ button_ref: Any = Field(validation_alias=AliasChoices("button_ref", "buttonRef", "button_id", "buttonId", "id"))
1430
+ placement: PublicButtonPlacement = Field(default=PublicButtonPlacement.detail, validation_alias=AliasChoices("placement", "position"))
1431
+ primary: bool = Field(default=False, validation_alias=AliasChoices("primary", "being_main", "beingMain"))
1432
+ button_limit: list[list[ViewFilterRulePatch]] = Field(
1433
+ default_factory=list,
1434
+ validation_alias=AliasChoices("button_limit", "buttonLimit", "visible_when", "visibleWhen"),
1435
+ )
1436
+ button_formula: str | None = Field(default=None, validation_alias=AliasChoices("button_formula", "buttonFormula"))
1437
+ button_formula_type: int = Field(default=1, validation_alias=AliasChoices("button_formula_type", "buttonFormulaType"))
1438
+ print_tpls: list[Any] = Field(default_factory=list, validation_alias=AliasChoices("print_tpls", "printTpls"))
1439
+
1440
+ @model_validator(mode="before")
1441
+ @classmethod
1442
+ def normalize_aliases(cls, value: Any) -> Any:
1443
+ if not isinstance(value, dict):
1444
+ return value
1445
+ payload = dict(value)
1446
+ raw_placement = payload.get("placement", payload.get("position", payload.get("config_type", payload.get("configType"))))
1447
+ if isinstance(raw_placement, str):
1448
+ normalized = raw_placement.strip().lower()
1449
+ if normalized in {"top", "header"}:
1450
+ payload["placement"] = PublicButtonPlacement.header.value
1451
+ elif normalized in {"detail", "data_detail"}:
1452
+ payload["placement"] = PublicButtonPlacement.detail.value
1453
+ elif normalized in {"list", "row", "row_action"}:
1454
+ payload["placement"] = PublicButtonPlacement.list.value
1455
+ raw_limits = payload.get("button_limit", payload.get("buttonLimit", payload.get("visible_when", payload.get("visibleWhen"))))
1456
+ if isinstance(raw_limits, list) and raw_limits and all(isinstance(item, dict) for item in raw_limits):
1457
+ payload["button_limit"] = [raw_limits]
1458
+ return payload
1459
+
1460
+
1461
+ class CustomButtonViewConfigPatch(StrictModel):
1462
+ view_key: str = Field(validation_alias=AliasChoices("view_key", "viewKey", "viewgraphKey", "viewGraphKey"))
1463
+ mode: str = Field(default="merge", validation_alias=AliasChoices("mode", "apply_mode", "applyMode"))
1464
+ buttons: list[CustomButtonViewButtonBindingPatch] = Field(default_factory=list)
1465
+
1466
+ @model_validator(mode="before")
1467
+ @classmethod
1468
+ def normalize_aliases(cls, value: Any) -> Any:
1469
+ if not isinstance(value, dict):
1470
+ return value
1471
+ return _normalize_builder_view_key_payload(dict(value))
1472
+
1473
+ @model_validator(mode="after")
1474
+ def validate_mode(self) -> "CustomButtonViewConfigPatch":
1475
+ normalized = str(self.mode or "").strip().lower()
1476
+ if normalized not in {"merge", "replace"}:
1477
+ raise ValueError("view_configs[].mode must be merge or replace")
1478
+ self.mode = normalized
1479
+ if normalized == "merge" and "buttons" not in getattr(self, "model_fields_set", set()):
1480
+ raise ValueError("view_configs[] in merge mode requires buttons; pass buttons: [] or mode=replace to clear existing bindings")
1481
+ return self
1482
+
1483
+
1484
+ class CustomButtonsApplyRequest(StrictModel):
1485
+ app_key: str
1486
+ upsert_buttons: list[CustomButtonUpsertPatch] = Field(default_factory=list)
1487
+ patch_buttons: list[CustomButtonPartialPatch] = Field(default_factory=list)
1488
+ remove_buttons: list[CustomButtonRemovePatch] = Field(default_factory=list)
1489
+ view_configs: list[CustomButtonViewConfigPatch] = Field(default_factory=list)
1490
+
1491
+ @model_validator(mode="before")
1492
+ @classmethod
1493
+ def normalize_aliases(cls, value: Any) -> Any:
1494
+ if not isinstance(value, dict):
1495
+ return value
1496
+ payload = dict(value)
1497
+ if "buttons" in payload and "upsert_buttons" not in payload:
1498
+ payload["upsert_buttons"] = payload.pop("buttons")
1499
+ if "upsertButtons" in payload and "upsert_buttons" not in payload:
1500
+ payload["upsert_buttons"] = payload.pop("upsertButtons")
1501
+ if "patchButtons" in payload and "patch_buttons" not in payload:
1502
+ payload["patch_buttons"] = payload.pop("patchButtons")
1503
+ if "removeButtons" in payload and "remove_buttons" not in payload:
1504
+ payload["remove_buttons"] = payload.pop("removeButtons")
1505
+ if "viewConfigs" in payload and "view_configs" not in payload:
1506
+ payload["view_configs"] = payload.pop("viewConfigs")
1507
+ return payload
1508
+
1509
+ @model_validator(mode="after")
1510
+ def validate_shape(self) -> "CustomButtonsApplyRequest":
1511
+ if not self.upsert_buttons and not self.patch_buttons and not self.remove_buttons and not self.view_configs:
1512
+ raise ValueError("custom button apply requires at least one upsert, patch, remove, or view config operation")
1513
+ return self
1514
+
1515
+
1516
+ class AssociatedResourceUpsertPatch(StrictModel):
1517
+ client_key: str | None = Field(default=None, validation_alias=AliasChoices("client_key", "clientKey"))
1518
+ associated_item_id: int | None = Field(
1519
+ default=None,
1520
+ validation_alias=AliasChoices("associated_item_id", "associatedItemId", "asosChartId", "id"),
1521
+ )
1522
+ graph_type: str = Field(validation_alias=AliasChoices("graph_type", "graphType"))
1523
+ target_app_key: str = Field(validation_alias=AliasChoices("target_app_key", "targetAppKey", "app_key", "appKey"))
1524
+ chart_key: str | None = Field(default=None, validation_alias=AliasChoices("chart_key", "chartKey"))
1525
+ view_key: str | None = Field(default=None, validation_alias=AliasChoices("view_key", "viewKey", "viewgraphKey", "viewGraphKey"))
1526
+ report_source: str | None = Field(default=None, validation_alias=AliasChoices("report_source", "reportSource"))
1527
+ source_type: str | None = Field(default=None, validation_alias=AliasChoices("source_type", "sourceType"))
1528
+ match_rules: list[CustomButtonMatchRulePatch] = Field(default_factory=list, validation_alias=AliasChoices("match_rules", "matchRules"))
1529
+ match_mappings: list[FieldMatchMappingPatch] = Field(
1530
+ default_factory=list,
1531
+ validation_alias=AliasChoices("match_mappings", "matchMappings"),
1532
+ )
1533
+
1534
+ @model_validator(mode="before")
1535
+ @classmethod
1536
+ def normalize_aliases(cls, value: Any) -> Any:
1537
+ if not isinstance(value, dict):
1538
+ return value
1539
+ payload = dict(value)
1540
+ payload = _normalize_builder_view_key_payload(payload)
1541
+ if "chart_id" in payload and "chart_key" not in payload and "chartKey" not in payload:
1542
+ payload["chart_key"] = str(payload.pop("chart_id"))
1543
+ raw_graph_type = str(payload.get("graph_type", payload.get("graphType", "")) or "").strip().lower()
1544
+ raw_source_type = payload.get("source_type", payload.get("sourceType"))
1545
+ has_report_source = "report_source" in payload or "reportSource" in payload
1546
+ if isinstance(raw_source_type, str):
1547
+ normalized_source = raw_source_type.strip().upper()
1548
+ if normalized_source == "BI_QINGFLOW" and not has_report_source:
1549
+ payload["report_source"] = "app"
1550
+ payload.pop("source_type", None)
1551
+ payload.pop("sourceType", None)
1552
+ elif normalized_source == "BI_DATASET" and not has_report_source:
1553
+ payload["report_source"] = "dataset"
1554
+ payload.pop("source_type", None)
1555
+ payload.pop("sourceType", None)
1556
+ elif normalized_source == "QINGFLOW" and raw_graph_type in {"view", "viewgraph"}:
1557
+ payload.pop("source_type", None)
1558
+ payload.pop("sourceType", None)
1559
+ return payload
1560
+
1561
+
1562
+ class AssociatedResourcePartialPatch(StrictModel):
1563
+ associated_item_id: int = Field(validation_alias=AliasChoices("associated_item_id", "associatedItemId", "asosChartId", "id"))
1564
+ client_key: str | None = Field(default=None, validation_alias=AliasChoices("client_key", "clientKey"))
1565
+ set: dict[str, Any] = Field(default_factory=dict, validation_alias=AliasChoices("set", "update", "values"))
1566
+ unset: list[str] = Field(default_factory=list, validation_alias=AliasChoices("unset", "clear", "remove"))
1567
+
1568
+ @model_validator(mode="after")
1569
+ def validate_shape(self) -> "AssociatedResourcePartialPatch":
1570
+ if self.associated_item_id <= 0:
1571
+ raise ValueError("patch_resources[].associated_item_id must be a positive integer")
1572
+ if not self.set and not self.unset:
1573
+ raise ValueError("patch_resources[] requires set or unset")
1574
+ return self
1575
+
1576
+
1577
+ class AssociatedResourceViewConfigPatch(StrictModel):
1578
+ view_key: str = Field(validation_alias=AliasChoices("view_key", "viewKey", "viewgraphKey", "viewGraphKey"))
1579
+ visible: bool = Field(default=True, validation_alias=AliasChoices("visible", "enabled", "asosChartVisible"))
1580
+ limit_type: str | None = Field(default=None, validation_alias=AliasChoices("limit_type", "limitType"))
1581
+ associated_item_ids: list[Any] = Field(
1582
+ default_factory=list,
1583
+ validation_alias=AliasChoices("associated_item_ids", "associatedItemIds", "asosChartIdList"),
1584
+ )
1585
+ associated_item_refs: list[str] = Field(default_factory=list, validation_alias=AliasChoices("associated_item_refs", "associatedItemRefs", "refs"))
1586
+
1587
+ @model_validator(mode="before")
1588
+ @classmethod
1589
+ def normalize_aliases(cls, value: Any) -> Any:
1590
+ if not isinstance(value, dict):
1591
+ return value
1592
+ return _normalize_builder_view_key_payload(dict(value))
1593
+
1594
+
1595
+ class AssociatedResourcesApplyRequest(StrictModel):
1596
+ app_key: str
1597
+ upsert_resources: list[AssociatedResourceUpsertPatch] = Field(default_factory=list)
1598
+ patch_resources: list[AssociatedResourcePartialPatch] = Field(default_factory=list)
1599
+ remove_associated_item_ids: list[Any] = Field(default_factory=list)
1600
+ reorder_associated_item_ids: list[Any] = Field(default_factory=list)
1601
+ view_configs: list[AssociatedResourceViewConfigPatch] = Field(default_factory=list)
1602
+
1603
+ @model_validator(mode="before")
1604
+ @classmethod
1605
+ def normalize_aliases(cls, value: Any) -> Any:
1606
+ if not isinstance(value, dict):
1607
+ return value
1608
+ payload = dict(value)
1609
+ if "upsertResources" in payload and "upsert_resources" not in payload:
1610
+ payload["upsert_resources"] = payload.pop("upsertResources")
1611
+ if "patchResources" in payload and "patch_resources" not in payload:
1612
+ payload["patch_resources"] = payload.pop("patchResources")
1613
+ if "resources" in payload and "upsert_resources" not in payload:
1614
+ payload["upsert_resources"] = payload.pop("resources")
1615
+ if "removeAssociatedItemIds" in payload and "remove_associated_item_ids" not in payload:
1616
+ payload["remove_associated_item_ids"] = payload.pop("removeAssociatedItemIds")
1617
+ if "reorderAssociatedItemIds" in payload and "reorder_associated_item_ids" not in payload:
1618
+ payload["reorder_associated_item_ids"] = payload.pop("reorderAssociatedItemIds")
1619
+ if "viewConfigs" in payload and "view_configs" not in payload:
1620
+ payload["view_configs"] = payload.pop("viewConfigs")
1621
+ return payload
1622
+
1623
+ @model_validator(mode="after")
1624
+ def validate_shape(self) -> "AssociatedResourcesApplyRequest":
1625
+ if not self.upsert_resources and not self.patch_resources and not self.remove_associated_item_ids and not self.reorder_associated_item_ids and not self.view_configs:
1626
+ raise ValueError("associated resources apply requires at least one upsert, patch, remove, reorder, or view config operation")
1627
+ return self
1628
+
1629
+
1244
1630
  class ViewButtonBindingPatch(StrictModel):
1245
1631
  button_type: PublicViewButtonType = Field(validation_alias=AliasChoices("button_type", "buttonType"))
1246
1632
  config_type: PublicViewButtonConfigType = Field(validation_alias=AliasChoices("config_type", "configType"))
@@ -1278,10 +1664,12 @@ class ViewButtonBindingPatch(StrictModel):
1278
1664
  raw_config_type = payload.get("config_type", payload.get("configType"))
1279
1665
  if isinstance(raw_config_type, str):
1280
1666
  normalized_config = raw_config_type.strip().lower()
1281
- if normalized_config == "top":
1667
+ if normalized_config in {"top", "header"}:
1282
1668
  payload["config_type"] = "TOP"
1283
1669
  elif normalized_config == "detail":
1284
1670
  payload["config_type"] = "DETAIL"
1671
+ elif normalized_config in {"inside", "list", "row", "row_action"}:
1672
+ payload["config_type"] = "INSIDE"
1285
1673
  raw_limits = payload.get("button_limit", payload.get("buttonLimit"))
1286
1674
  if isinstance(raw_limits, list) and raw_limits and all(isinstance(item, dict) for item in raw_limits):
1287
1675
  payload["button_limit"] = [raw_limits]
@@ -1407,10 +1795,18 @@ class ChartUpsertPatch(StrictModel):
1407
1795
  normalized = raw_type.strip().lower()
1408
1796
  aliases = {
1409
1797
  "targetchart": PublicChartType.target.value,
1798
+ "indicatorchart": PublicChartType.indicator.value,
1799
+ "summarychart": PublicChartType.summary.value,
1410
1800
  "piechart": PublicChartType.pie.value,
1411
1801
  "barchart": PublicChartType.bar.value,
1802
+ "columnchart": PublicChartType.columnar.value,
1803
+ "columnarchart": PublicChartType.columnar.value,
1412
1804
  "linechart": PublicChartType.line.value,
1413
1805
  "tablechart": PublicChartType.table.value,
1806
+ "detailchart": PublicChartType.detail.value,
1807
+ "percent_stacked_column": PublicChartType.pct_stack_col.value,
1808
+ "percent_stacked_bar": PublicChartType.pct_stack_bar.value,
1809
+ "percent_stacked_area": PublicChartType.pct_stack_area.value,
1414
1810
  }
1415
1811
  if normalized in aliases:
1416
1812
  payload["chart_type"] = aliases[normalized]
@@ -1423,9 +1819,39 @@ class ChartUpsertPatch(StrictModel):
1423
1819
  return payload
1424
1820
 
1425
1821
 
1822
+ class ChartPartialPatch(StrictModel):
1823
+ chart_id: str | None = None
1824
+ name: str | None = None
1825
+ set: dict[str, Any] = Field(default_factory=dict, validation_alias=AliasChoices("set", "update", "values"))
1826
+ unset: list[str] = Field(default_factory=list, validation_alias=AliasChoices("unset", "clear", "remove"))
1827
+
1828
+ @model_validator(mode="before")
1829
+ @classmethod
1830
+ def normalize_aliases(cls, value: Any) -> Any:
1831
+ if not isinstance(value, dict):
1832
+ return value
1833
+ payload = dict(value)
1834
+ if "id" in payload and "chart_id" not in payload:
1835
+ payload["chart_id"] = str(payload.pop("id"))
1836
+ if "chart_name" in payload and "name" not in payload:
1837
+ payload["name"] = payload.pop("chart_name")
1838
+ if "chartName" in payload and "name" not in payload:
1839
+ payload["name"] = payload.pop("chartName")
1840
+ return payload
1841
+
1842
+ @model_validator(mode="after")
1843
+ def validate_shape(self) -> "ChartPartialPatch":
1844
+ if not str(self.chart_id or "").strip() and not str(self.name or "").strip():
1845
+ raise ValueError("patch_charts[] requires chart_id or unique name")
1846
+ if not self.set and not self.unset:
1847
+ raise ValueError("patch_charts[] requires set or unset")
1848
+ return self
1849
+
1850
+
1426
1851
  class ChartApplyRequest(StrictModel):
1427
1852
  app_key: str
1428
1853
  upsert_charts: list[ChartUpsertPatch] = Field(default_factory=list)
1854
+ patch_charts: list[ChartPartialPatch] = Field(default_factory=list)
1429
1855
  remove_chart_ids: list[str] = Field(default_factory=list)
1430
1856
  reorder_chart_ids: list[str] = Field(default_factory=list)
1431
1857
 
@@ -1435,6 +1861,14 @@ class ChartApplyRequest(StrictModel):
1435
1861
  if not isinstance(value, dict):
1436
1862
  return value
1437
1863
  payload = dict(value)
1864
+ if "upsertCharts" in payload and "upsert_charts" not in payload:
1865
+ payload["upsert_charts"] = payload.pop("upsertCharts")
1866
+ if "patchCharts" in payload and "patch_charts" not in payload:
1867
+ payload["patch_charts"] = payload.pop("patchCharts")
1868
+ if "removeChartIds" in payload and "remove_chart_ids" not in payload:
1869
+ payload["remove_chart_ids"] = payload.pop("removeChartIds")
1870
+ if "reorderChartIds" in payload and "reorder_chart_ids" not in payload:
1871
+ payload["reorder_chart_ids"] = payload.pop("reorderChartIds")
1438
1872
  for key in ("remove_chart_ids", "reorder_chart_ids"):
1439
1873
  raw = payload.get(key)
1440
1874
  if isinstance(raw, list):
@@ -1443,8 +1877,8 @@ class ChartApplyRequest(StrictModel):
1443
1877
 
1444
1878
  @model_validator(mode="after")
1445
1879
  def validate_shape(self) -> "ChartApplyRequest":
1446
- if not self.upsert_charts and not self.remove_chart_ids and not self.reorder_chart_ids:
1447
- raise ValueError("chart apply requires at least one upsert, remove, or reorder operation")
1880
+ if not self.upsert_charts and not self.patch_charts and not self.remove_chart_ids and not self.reorder_chart_ids:
1881
+ raise ValueError("chart apply requires at least one upsert, patch, remove, or reorder operation")
1448
1882
  return self
1449
1883
 
1450
1884
 
@@ -1455,8 +1889,9 @@ class PortalComponentPositionPatch(StrictModel):
1455
1889
  pc_h: int = Field(default=8, validation_alias=AliasChoices("pc_h", "pcH", "h"))
1456
1890
  mobile_x: int = Field(default=0, validation_alias=AliasChoices("mobile_x", "mobileX"))
1457
1891
  mobile_y: int = Field(default=0, validation_alias=AliasChoices("mobile_y", "mobileY"))
1458
- mobile_w: int = Field(default=12, validation_alias=AliasChoices("mobile_w", "mobileW"))
1892
+ mobile_w: int = Field(default=6, validation_alias=AliasChoices("mobile_w", "mobileW"))
1459
1893
  mobile_h: int = Field(default=8, validation_alias=AliasChoices("mobile_h", "mobileH"))
1894
+ mobile_provided: bool = Field(default=False, exclude=True)
1460
1895
 
1461
1896
  @model_validator(mode="before")
1462
1897
  @classmethod
@@ -1466,6 +1901,8 @@ class PortalComponentPositionPatch(StrictModel):
1466
1901
  payload = dict(value)
1467
1902
  pc = payload.pop("pc", None)
1468
1903
  mobile = payload.pop("mobile", None)
1904
+ mobile_keys = {"mobile_x", "mobileX", "mobile_y", "mobileY", "mobile_w", "mobileW", "mobile_h", "mobileH"}
1905
+ mobile_provided = isinstance(mobile, dict) or any(key in payload for key in mobile_keys)
1469
1906
  if isinstance(pc, dict):
1470
1907
  if "pc_x" not in payload and "x" in pc:
1471
1908
  payload["pc_x"] = pc.get("x")
@@ -1484,18 +1921,40 @@ class PortalComponentPositionPatch(StrictModel):
1484
1921
  payload["mobile_w"] = mobile.get("cols")
1485
1922
  if "mobile_h" not in payload and "rows" in mobile:
1486
1923
  payload["mobile_h"] = mobile.get("rows")
1924
+ payload["mobile_provided"] = mobile_provided
1487
1925
  return payload
1488
1926
 
1489
1927
 
1490
1928
  class PortalChartRefPatch(StrictModel):
1491
- app_key: str
1929
+ app_key: str | None = None
1492
1930
  chart_id: str | None = None
1931
+ chart_key: str | None = None
1493
1932
  chart_name: str | None = None
1494
1933
 
1934
+ @model_validator(mode="before")
1935
+ @classmethod
1936
+ def normalize_aliases(cls, value: Any) -> Any:
1937
+ if not isinstance(value, dict):
1938
+ return value
1939
+ payload = dict(value)
1940
+ if "chartKey" in payload and "chart_key" not in payload:
1941
+ payload["chart_key"] = payload.pop("chartKey")
1942
+ if "chartId" in payload and "chart_id" not in payload:
1943
+ payload["chart_id"] = payload.pop("chartId")
1944
+ if "biChartId" in payload and "chart_id" not in payload:
1945
+ payload["chart_id"] = payload.pop("biChartId")
1946
+ if "chartName" in payload and "chart_name" not in payload:
1947
+ payload["chart_name"] = payload.pop("chartName")
1948
+ if "appKey" in payload and "app_key" not in payload:
1949
+ payload["app_key"] = payload.pop("appKey")
1950
+ return payload
1951
+
1495
1952
  @model_validator(mode="after")
1496
1953
  def validate_target(self) -> "PortalChartRefPatch":
1497
- if not (self.chart_id or self.chart_name):
1498
- raise ValueError("chart_ref requires chart_id or chart_name")
1954
+ if not (self.chart_id or self.chart_key or self.chart_name):
1955
+ raise ValueError("chart_ref requires chart_id, chart_key, or chart_name")
1956
+ if self.chart_name and not self.app_key and not (self.chart_id or self.chart_key):
1957
+ raise ValueError("chart_ref with chart_name requires app_key unless chart_id/chart_key is also provided")
1499
1958
  return self
1500
1959
 
1501
1960
 
@@ -1504,6 +1963,13 @@ class PortalViewRefPatch(StrictModel):
1504
1963
  view_key: str | None = None
1505
1964
  view_name: str | None = None
1506
1965
 
1966
+ @model_validator(mode="before")
1967
+ @classmethod
1968
+ def normalize_aliases(cls, value: Any) -> Any:
1969
+ if not isinstance(value, dict):
1970
+ return value
1971
+ return _normalize_builder_view_key_payload(dict(value))
1972
+
1507
1973
  @model_validator(mode="after")
1508
1974
  def validate_target(self) -> "PortalViewRefPatch":
1509
1975
  if not (self.view_key or self.view_name):
@@ -1558,9 +2024,10 @@ class PortalSectionPatch(StrictModel):
1558
2024
  class PortalApplyRequest(StrictModel):
1559
2025
  dash_key: str | None = None
1560
2026
  dash_name: str | None = None
1561
- package_tag_id: int | None = None
2027
+ package_tag_id: int | None = Field(default=None, validation_alias=AliasChoices("package_tag_id", "packageTagId", "package_id", "packageId"))
1562
2028
  publish: bool = True
1563
2029
  sections: list[PortalSectionPatch] = Field(default_factory=list)
2030
+ layout_preset: str | None = Field(default=None, validation_alias=AliasChoices("layout_preset", "layoutPreset"))
1564
2031
  visibility: VisibilityPatch | None = None
1565
2032
  auth: dict[str, Any] | None = None
1566
2033
  icon: str | None = None
@@ -1569,6 +2036,33 @@ class PortalApplyRequest(StrictModel):
1569
2036
  dash_global_config: dict[str, Any] | None = Field(default=None, validation_alias=AliasChoices("dash_global_config", "dashGlobalConfig"))
1570
2037
  config: dict[str, Any] = Field(default_factory=dict)
1571
2038
 
2039
+ @model_validator(mode="before")
2040
+ @classmethod
2041
+ def normalize_compat_payload(cls, value: Any) -> Any:
2042
+ if not isinstance(value, dict):
2043
+ return value
2044
+ payload = dict(value)
2045
+ if "dash_name" not in payload and "dashName" not in payload and "name" in payload:
2046
+ payload["dash_name"] = payload.pop("name")
2047
+ if "sections" not in payload and "pages" in payload:
2048
+ pages = payload.pop("pages")
2049
+ if not isinstance(pages, list):
2050
+ raise ValueError("portal pages must be a list")
2051
+ if len(pages) != 1:
2052
+ raise ValueError("portal_apply currently supports a single page; pass one page or flatten components into sections")
2053
+ page = pages[0]
2054
+ if not isinstance(page, dict):
2055
+ raise ValueError("portal pages[0] must be an object")
2056
+ components = page.get("components")
2057
+ if not isinstance(components, list) or not components:
2058
+ raise ValueError("portal pages[0].components must be a non-empty list")
2059
+ payload["sections"] = components
2060
+ if "theme" in payload:
2061
+ payload.pop("theme")
2062
+ if "type" in payload:
2063
+ payload.pop("type")
2064
+ return payload
2065
+
1572
2066
  @model_validator(mode="after")
1573
2067
  def validate_shape(self) -> "PortalApplyRequest":
1574
2068
  if not self.dash_key and not self.package_tag_id:
@@ -1579,6 +2073,8 @@ class PortalApplyRequest(StrictModel):
1579
2073
  raise ValueError("portal apply requires a non-empty sections list when creating a portal")
1580
2074
  if self.visibility is not None and self.auth is not None:
1581
2075
  raise ValueError("visibility and auth cannot be provided together")
2076
+ if self.layout_preset is not None and self.layout_preset not in {"auto", "dashboard_2col", "dashboard_3col"}:
2077
+ raise ValueError("layout_preset must be one of: auto, dashboard_2col, dashboard_3col")
1582
2078
  return self
1583
2079
 
1584
2080
 
@@ -1589,23 +2085,38 @@ FieldUpdatePatch.model_rebuild()
1589
2085
 
1590
2086
  class AppGetResponse(StrictModel):
1591
2087
  app_key: str
2088
+ app_name: str | None = None
2089
+ name: str | None = None
1592
2090
  title: str | None = None
1593
2091
  app_icon: str | None = None
2092
+ icon_config: dict[str, Any] = Field(default_factory=dict)
1594
2093
  visibility: dict[str, Any] = Field(default_factory=dict)
1595
2094
  tag_ids: list[int] = Field(default_factory=list)
1596
2095
  publish_status: int | None = None
1597
2096
  field_count: int = 0
1598
2097
  layout_section_count: int = 0
1599
2098
  view_count: int = 0
2099
+ chart_count: int = 0
2100
+ associated_resource_count: int = 0
2101
+ custom_button_count: int = 0
1600
2102
  workflow_enabled: bool = False
2103
+ counts: dict[str, int] = Field(default_factory=dict)
2104
+ views: list[dict[str, Any]] = Field(default_factory=list)
2105
+ charts: list[dict[str, Any]] = Field(default_factory=list)
2106
+ associated_resources: list[dict[str, Any]] = Field(default_factory=list)
2107
+ custom_buttons: list[dict[str, Any]] = Field(default_factory=list)
1601
2108
  verification_hints: list[str] = Field(default_factory=list)
1602
2109
  editability: dict[str, bool | None] = Field(default_factory=dict)
2110
+ form_settings: dict[str, Any] = Field(default_factory=dict)
1603
2111
 
1604
2112
 
1605
2113
  class AppGetFieldsResponse(StrictModel):
1606
2114
  app_key: str
1607
2115
  fields: list[dict[str, Any]] = Field(default_factory=list)
1608
2116
  field_count: int = 0
2117
+ chart_fields: list[dict[str, Any]] = Field(default_factory=list)
2118
+ chart_field_count: int = 0
2119
+ form_settings: dict[str, Any] = Field(default_factory=dict)
1609
2120
 
1610
2121
 
1611
2122
  class AppGetLayoutResponse(StrictModel):
@@ -1637,7 +2148,6 @@ AppReadSummaryResponse = AppGetResponse
1637
2148
  AppFieldsReadResponse = AppGetFieldsResponse
1638
2149
  AppLayoutReadResponse = AppGetLayoutResponse
1639
2150
  AppViewsReadResponse = AppGetViewsResponse
1640
- AppFlowReadResponse = AppGetFlowResponse
1641
2151
  AppChartsReadResponse = AppGetChartsResponse
1642
2152
 
1643
2153
 
@@ -1652,6 +2162,7 @@ class PortalReadSummaryResponse(StrictModel):
1652
2162
  dash_name: str | None = None
1653
2163
  package_tag_ids: list[int] = Field(default_factory=list)
1654
2164
  dash_icon: str | None = None
2165
+ icon_config: dict[str, Any] = Field(default_factory=dict)
1655
2166
  hide_copyright: bool | None = None
1656
2167
  config_keys: list[str] = Field(default_factory=list)
1657
2168
  dash_global_config_keys: list[str] = Field(default_factory=list)
@@ -1665,6 +2176,7 @@ class PortalGetResponse(StrictModel):
1665
2176
  dash_name: str | None = None
1666
2177
  package_tag_ids: list[int] = Field(default_factory=list)
1667
2178
  dash_icon: str | None = None
2179
+ icon_config: dict[str, Any] = Field(default_factory=dict)
1668
2180
  hide_copyright: bool | None = None
1669
2181
  visibility: dict[str, Any] = Field(default_factory=dict)
1670
2182
  auth: dict[str, Any] = Field(default_factory=dict)
@@ -1681,6 +2193,8 @@ class ViewGetResponse(StrictModel):
1681
2193
  config: dict[str, Any] = Field(default_factory=dict)
1682
2194
  questions: list[dict[str, Any]] = Field(default_factory=list)
1683
2195
  associations: list[dict[str, Any]] = Field(default_factory=list)
2196
+ buttons_config: dict[str, Any] = Field(default_factory=dict)
2197
+ associated_resources_config: dict[str, Any] = Field(default_factory=dict)
1684
2198
 
1685
2199
 
1686
2200
  class ChartGetResponse(StrictModel):
@@ -1720,43 +2234,10 @@ class LayoutPlanRequest(StrictModel):
1720
2234
  return payload
1721
2235
 
1722
2236
 
1723
- class FlowPlanRequest(StrictModel):
1724
- app_key: str
1725
- mode: str = "replace"
1726
- nodes: list[FlowNodePatch] = Field(default_factory=list)
1727
- transitions: list[FlowTransitionPatch] = Field(default_factory=list)
1728
- preset: FlowPreset | None = None
1729
-
1730
- @model_validator(mode="before")
1731
- @classmethod
1732
- def normalize_mode_alias(cls, value: Any) -> Any:
1733
- if not isinstance(value, dict):
1734
- return value
1735
- payload = dict(value)
1736
- if str(payload.get("mode") or "").strip().lower() == "overwrite":
1737
- payload["mode"] = "replace"
1738
- raw_preset = payload.get("preset")
1739
- if raw_preset is None and isinstance(payload.get("base_preset"), str):
1740
- raw_preset = payload["base_preset"]
1741
- payload["preset"] = raw_preset
1742
- if isinstance(raw_preset, str):
1743
- normalized_preset = raw_preset.strip().lower()
1744
- preset_aliases = {
1745
- "default_approval": FlowPreset.basic_approval.value,
1746
- "approval": FlowPreset.basic_approval.value,
1747
- "basic approval": FlowPreset.basic_approval.value,
1748
- "default_fill_then_approve": FlowPreset.basic_fill_then_approve.value,
1749
- "default-fill-then-approve": FlowPreset.basic_fill_then_approve.value,
1750
- "fill_then_approve": FlowPreset.basic_fill_then_approve.value,
1751
- }
1752
- if normalized_preset in preset_aliases:
1753
- payload["preset"] = preset_aliases[normalized_preset]
1754
- return payload
1755
-
1756
-
1757
2237
  class ViewsPlanRequest(StrictModel):
1758
2238
  app_key: str
1759
2239
  upsert_views: list[ViewUpsertPatch] = Field(default_factory=list)
2240
+ patch_views: list[ViewPartialPatch] = Field(default_factory=list)
1760
2241
  remove_views: list[str] = Field(default_factory=list)
1761
2242
  preset: ViewsPreset | None = None
1762
2243
 
@@ -1879,7 +2360,10 @@ def _normalize_public_department_scope_mode(value: Any) -> str | None:
1879
2360
 
1880
2361
 
1881
2362
  CustomButtonMatchRulePatch.model_rebuild()
2363
+ CustomButtonFieldMappingPatch.model_rebuild()
1882
2364
  CustomButtonAddDataConfigPatch.model_rebuild()
2365
+ CustomButtonViewButtonBindingPatch.model_rebuild()
2366
+ CustomButtonViewConfigPatch.model_rebuild()
1883
2367
  CodeBlockAliasPathPatch.model_rebuild()
1884
2368
  ViewButtonBindingPatch.model_rebuild()
1885
2369
  ViewUpsertPatch.model_rebuild()