@qingflow-tech/qingflow-app-builder-mcp 1.0.6 → 1.0.8

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.
@@ -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,6 +131,13 @@ 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):
@@ -739,6 +762,8 @@ class FieldPatch(StrictModel):
739
762
  default=None,
740
763
  validation_alias=AliasChoices("custom_button_text", "customButtonText", "custom_btn_text", "customBtnText"),
741
764
  )
765
+ as_data_title: bool | None = Field(default=None, validation_alias=AliasChoices("as_data_title", "asDataTitle"))
766
+ as_data_cover: bool | None = Field(default=None, validation_alias=AliasChoices("as_data_cover", "asDataCover"))
742
767
  subfields: list["FieldPatch"] = Field(default_factory=list)
743
768
 
744
769
  @model_validator(mode="after")
@@ -819,6 +844,8 @@ class FieldMutation(StrictModel):
819
844
  default=None,
820
845
  validation_alias=AliasChoices("custom_button_text", "customButtonText", "custom_btn_text", "customBtnText"),
821
846
  )
847
+ as_data_title: bool | None = Field(default=None, validation_alias=AliasChoices("as_data_title", "asDataTitle"))
848
+ as_data_cover: bool | None = Field(default=None, validation_alias=AliasChoices("as_data_cover", "asDataCover"))
822
849
  subfields: list[FieldPatch] | None = None
823
850
  subfield_updates: list["FieldUpdatePatch"] | None = Field(
824
851
  default=None,
@@ -1013,6 +1040,27 @@ class FlowTransitionPatch(StrictModel):
1013
1040
  target: str = Field(alias="to")
1014
1041
 
1015
1042
 
1043
+ class ViewQueryConditionsPatch(StrictModel):
1044
+ enabled: bool = Field(default=True, validation_alias=AliasChoices("enabled", "queryConditionStatus", "status"))
1045
+ exact: bool = Field(default=False, validation_alias=AliasChoices("exact", "queryConditionExact"))
1046
+ hide_before_query: bool = Field(default=False, validation_alias=AliasChoices("hide_before_query", "hideBeforeQuery", "hideBeforeQueryCondition"))
1047
+ rows: list[list[str | int]] = Field(default_factory=list, validation_alias=AliasChoices("rows", "fields", "queryCondition"))
1048
+
1049
+
1050
+ class ViewAssociatedResourcesPatch(StrictModel):
1051
+ visible: bool = Field(default=True, validation_alias=AliasChoices("visible", "enabled", "asosChartVisible"))
1052
+ limit_type: str | None = Field(default=None, validation_alias=AliasChoices("limit_type", "limitType"))
1053
+ associated_item_ids: list[Any] = Field(
1054
+ default_factory=list,
1055
+ validation_alias=AliasChoices(
1056
+ "associated_item_ids",
1057
+ "associatedItemIds",
1058
+ "asosChartIdList",
1059
+ "items",
1060
+ ),
1061
+ )
1062
+
1063
+
1016
1064
  class ViewUpsertPatch(StrictModel):
1017
1065
  name: str
1018
1066
  view_key: str | None = Field(default=None, validation_alias=AliasChoices("view_key", "viewKey"))
@@ -1025,6 +1073,22 @@ class ViewUpsertPatch(StrictModel):
1025
1073
  title_field: str | None = Field(default=None, validation_alias=AliasChoices("title_field", "titleField"))
1026
1074
  buttons: list["ViewButtonBindingPatch"] | None = None
1027
1075
  visibility: VisibilityPatch | None = None
1076
+ query_conditions: ViewQueryConditionsPatch | None = Field(default=None, validation_alias=AliasChoices("query_conditions", "queryConditions", "query_condition", "queryCondition"))
1077
+ associated_resources: ViewAssociatedResourcesPatch | None = Field(
1078
+ default=None,
1079
+ validation_alias=AliasChoices(
1080
+ "associated_resources",
1081
+ "associatedResources",
1082
+ "associated_reports",
1083
+ "associatedReports",
1084
+ "asosChartConfig",
1085
+ ),
1086
+ )
1087
+ partial_update: bool = Field(default=False, exclude=True, validation_alias=AliasChoices("_partial_update", "partial_update", "partialUpdate"))
1088
+ preserve_filters: bool = Field(default=False, exclude=True, validation_alias=AliasChoices("_preserve_filters", "preserve_filters", "preserveFilters"))
1089
+ preserve_buttons: bool = Field(default=False, exclude=True, validation_alias=AliasChoices("_preserve_buttons", "preserve_buttons", "preserveButtons"))
1090
+ preserve_query_conditions: bool = Field(default=False, exclude=True, validation_alias=AliasChoices("_preserve_query_conditions", "preserve_query_conditions", "preserveQueryConditions"))
1091
+ preserve_associated_resources: bool = Field(default=False, exclude=True, validation_alias=AliasChoices("_preserve_associated_resources", "preserve_associated_resources", "preserveAssociatedResources"))
1028
1092
 
1029
1093
  @model_validator(mode="before")
1030
1094
  @classmethod
@@ -1032,6 +1096,7 @@ class ViewUpsertPatch(StrictModel):
1032
1096
  if not isinstance(value, dict):
1033
1097
  return value
1034
1098
  payload = dict(value)
1099
+ payload = _normalize_builder_view_key_payload(payload)
1035
1100
  if "fields" in payload and "columns" not in payload:
1036
1101
  payload["columns"] = payload.pop("fields")
1037
1102
  if "column_names" in payload and "columns" not in payload:
@@ -1042,6 +1107,20 @@ class ViewUpsertPatch(StrictModel):
1042
1107
  payload["filters"] = payload.pop("filter_rules")
1043
1108
  if "filterRules" in payload and "filters" not in payload:
1044
1109
  payload["filters"] = payload.pop("filterRules")
1110
+ if "queryConditions" in payload and "query_conditions" not in payload:
1111
+ payload["query_conditions"] = payload.pop("queryConditions")
1112
+ if "query_condition" in payload and "query_conditions" not in payload:
1113
+ payload["query_conditions"] = payload.pop("query_condition")
1114
+ if "queryCondition" in payload and "query_conditions" not in payload:
1115
+ payload["query_conditions"] = payload.pop("queryCondition")
1116
+ if "associatedResources" in payload and "associated_resources" not in payload:
1117
+ payload["associated_resources"] = payload.pop("associatedResources")
1118
+ if "associated_reports" in payload and "associated_resources" not in payload:
1119
+ payload["associated_resources"] = payload.pop("associated_reports")
1120
+ if "associatedReports" in payload and "associated_resources" not in payload:
1121
+ payload["associated_resources"] = payload.pop("associatedReports")
1122
+ if "asosChartConfig" in payload and "associated_resources" not in payload:
1123
+ payload["associated_resources"] = payload.pop("asosChartConfig")
1045
1124
  raw_type = payload.get("type")
1046
1125
  if isinstance(raw_type, str):
1047
1126
  normalized = raw_type.strip().lower()
@@ -1066,6 +1145,28 @@ class ViewUpsertPatch(StrictModel):
1066
1145
  return self
1067
1146
 
1068
1147
 
1148
+ class ViewPartialPatch(StrictModel):
1149
+ view_key: str | None = Field(default=None, validation_alias=AliasChoices("view_key", "viewKey", "viewgraphKey", "viewGraphKey"))
1150
+ name: str | None = Field(default=None, validation_alias=AliasChoices("name", "view_name", "viewName"))
1151
+ set: dict[str, Any] = Field(default_factory=dict, validation_alias=AliasChoices("set", "update", "values"))
1152
+ unset: list[str] = Field(default_factory=list, validation_alias=AliasChoices("unset", "clear", "remove"))
1153
+
1154
+ @model_validator(mode="before")
1155
+ @classmethod
1156
+ def normalize_aliases(cls, value: Any) -> Any:
1157
+ if not isinstance(value, dict):
1158
+ return value
1159
+ return _normalize_builder_view_key_payload(dict(value))
1160
+
1161
+ @model_validator(mode="after")
1162
+ def validate_shape(self) -> "ViewPartialPatch":
1163
+ if not str(self.view_key or "").strip() and not str(self.name or "").strip():
1164
+ raise ValueError("patch_views[] requires view_key or unique name")
1165
+ if not self.set and not self.unset:
1166
+ raise ValueError("patch_views[] requires set or unset")
1167
+ return self
1168
+
1169
+
1069
1170
  class CustomButtonJudgeValuePatch(StrictModel):
1070
1171
  id: int | str | None = None
1071
1172
  value: Any | None = None
@@ -1128,13 +1229,44 @@ class CustomButtonMatchRulePatch(StrictModel):
1128
1229
  return payload
1129
1230
 
1130
1231
 
1232
+ class CustomButtonFieldMappingPatch(StrictModel):
1233
+ source_field: Any = Field(validation_alias=AliasChoices("source_field", "sourceField", "source"))
1234
+ target_field: Any = Field(validation_alias=AliasChoices("target_field", "targetField", "target"))
1235
+
1236
+
1237
+ class FieldMatchMappingPatch(StrictModel):
1238
+ target_field: Any = Field(validation_alias=AliasChoices("target_field", "targetField", "target", "field"))
1239
+ source_field: Any | None = Field(default=None, validation_alias=AliasChoices("source_field", "sourceField", "source"))
1240
+ value: Any | None = Field(default=None, validation_alias=AliasChoices("value", "static_value", "staticValue"))
1241
+ operator: str = Field(default="eq", validation_alias=AliasChoices("operator", "op", "judge_type", "judgeType"))
1242
+
1243
+ @model_validator(mode="after")
1244
+ def validate_shape(self) -> "FieldMatchMappingPatch":
1245
+ has_source = self.source_field is not None and str(self.source_field).strip() != ""
1246
+ has_value = self.value is not None
1247
+ if has_source == has_value:
1248
+ raise ValueError("match_mappings[] requires exactly one of source_field or value")
1249
+ return self
1250
+
1251
+
1131
1252
  class CustomButtonAddDataConfigPatch(StrictModel):
1132
- related_app_key: str | None = Field(default=None, validation_alias=AliasChoices("related_app_key", "relatedAppKey"))
1253
+ related_app_key: str | None = Field(
1254
+ default=None,
1255
+ validation_alias=AliasChoices("related_app_key", "relatedAppKey", "target_app_key", "targetAppKey"),
1256
+ )
1133
1257
  related_app_name: str | None = Field(default=None, validation_alias=AliasChoices("related_app_name", "relatedAppName"))
1134
1258
  que_relation: list[CustomButtonMatchRulePatch] = Field(
1135
1259
  default_factory=list,
1136
1260
  validation_alias=AliasChoices("que_relation", "queRelation"),
1137
1261
  )
1262
+ field_mappings: list[CustomButtonFieldMappingPatch] = Field(
1263
+ default_factory=list,
1264
+ validation_alias=AliasChoices("field_mappings", "fieldMappings", "mappings"),
1265
+ )
1266
+ default_values: dict[str, Any] = Field(
1267
+ default_factory=dict,
1268
+ validation_alias=AliasChoices("default_values", "defaultValues", "defaults"),
1269
+ )
1138
1270
 
1139
1271
 
1140
1272
  class CustomButtonExternalQRobotConfigPatch(StrictModel):
@@ -1241,6 +1373,240 @@ class CustomButtonPatch(StrictModel):
1241
1373
  return self
1242
1374
 
1243
1375
 
1376
+ class CustomButtonUpsertPatch(CustomButtonPatch):
1377
+ button_id: int | None = Field(default=None, validation_alias=AliasChoices("button_id", "buttonId", "id"))
1378
+ client_key: str | None = Field(default=None, validation_alias=AliasChoices("client_key", "clientKey"))
1379
+
1380
+
1381
+ class CustomButtonPartialPatch(StrictModel):
1382
+ button_id: int | None = Field(default=None, validation_alias=AliasChoices("button_id", "buttonId", "id"))
1383
+ button_text: str | None = Field(default=None, validation_alias=AliasChoices("button_text", "buttonText", "name"))
1384
+ client_key: str | None = Field(default=None, validation_alias=AliasChoices("client_key", "clientKey"))
1385
+ set: dict[str, Any] = Field(default_factory=dict, validation_alias=AliasChoices("set", "update", "values"))
1386
+ unset: list[str] = Field(default_factory=list, validation_alias=AliasChoices("unset", "clear", "remove"))
1387
+
1388
+ @model_validator(mode="after")
1389
+ def validate_shape(self) -> "CustomButtonPartialPatch":
1390
+ if self.button_id is None and not str(self.button_text or "").strip():
1391
+ raise ValueError("patch_buttons[] requires button_id or unique button_text")
1392
+ if not self.set and not self.unset:
1393
+ raise ValueError("patch_buttons[] requires set or unset")
1394
+ return self
1395
+
1396
+
1397
+ class CustomButtonRemovePatch(StrictModel):
1398
+ button_id: int | None = Field(default=None, validation_alias=AliasChoices("button_id", "buttonId", "id"))
1399
+ button_text: str | None = Field(default=None, validation_alias=AliasChoices("button_text", "buttonText", "name"))
1400
+
1401
+ @model_validator(mode="after")
1402
+ def validate_selector(self) -> "CustomButtonRemovePatch":
1403
+ if self.button_id is None and not str(self.button_text or "").strip():
1404
+ raise ValueError("remove_buttons[] requires button_id or button_text")
1405
+ return self
1406
+
1407
+
1408
+ class CustomButtonViewButtonBindingPatch(StrictModel):
1409
+ button_ref: Any = Field(validation_alias=AliasChoices("button_ref", "buttonRef", "button_id", "buttonId", "id"))
1410
+ placement: PublicButtonPlacement = Field(default=PublicButtonPlacement.detail, validation_alias=AliasChoices("placement", "position"))
1411
+ primary: bool = Field(default=False, validation_alias=AliasChoices("primary", "being_main", "beingMain"))
1412
+ button_limit: list[list[ViewFilterRulePatch]] = Field(
1413
+ default_factory=list,
1414
+ validation_alias=AliasChoices("button_limit", "buttonLimit", "visible_when", "visibleWhen"),
1415
+ )
1416
+ button_formula: str | None = Field(default=None, validation_alias=AliasChoices("button_formula", "buttonFormula"))
1417
+ button_formula_type: int = Field(default=1, validation_alias=AliasChoices("button_formula_type", "buttonFormulaType"))
1418
+ print_tpls: list[Any] = Field(default_factory=list, validation_alias=AliasChoices("print_tpls", "printTpls"))
1419
+
1420
+ @model_validator(mode="before")
1421
+ @classmethod
1422
+ def normalize_aliases(cls, value: Any) -> Any:
1423
+ if not isinstance(value, dict):
1424
+ return value
1425
+ payload = dict(value)
1426
+ raw_placement = payload.get("placement", payload.get("position", payload.get("config_type", payload.get("configType"))))
1427
+ if isinstance(raw_placement, str):
1428
+ normalized = raw_placement.strip().lower()
1429
+ if normalized in {"top", "header"}:
1430
+ payload["placement"] = PublicButtonPlacement.header.value
1431
+ elif normalized in {"detail", "data_detail"}:
1432
+ payload["placement"] = PublicButtonPlacement.detail.value
1433
+ elif normalized in {"list", "row", "row_action"}:
1434
+ payload["placement"] = PublicButtonPlacement.list.value
1435
+ raw_limits = payload.get("button_limit", payload.get("buttonLimit", payload.get("visible_when", payload.get("visibleWhen"))))
1436
+ if isinstance(raw_limits, list) and raw_limits and all(isinstance(item, dict) for item in raw_limits):
1437
+ payload["button_limit"] = [raw_limits]
1438
+ return payload
1439
+
1440
+
1441
+ class CustomButtonViewConfigPatch(StrictModel):
1442
+ view_key: str = Field(validation_alias=AliasChoices("view_key", "viewKey", "viewgraphKey", "viewGraphKey"))
1443
+ mode: str = Field(default="merge", validation_alias=AliasChoices("mode", "apply_mode", "applyMode"))
1444
+ buttons: list[CustomButtonViewButtonBindingPatch] = Field(default_factory=list)
1445
+
1446
+ @model_validator(mode="before")
1447
+ @classmethod
1448
+ def normalize_aliases(cls, value: Any) -> Any:
1449
+ if not isinstance(value, dict):
1450
+ return value
1451
+ return _normalize_builder_view_key_payload(dict(value))
1452
+
1453
+ @model_validator(mode="after")
1454
+ def validate_mode(self) -> "CustomButtonViewConfigPatch":
1455
+ normalized = str(self.mode or "").strip().lower()
1456
+ if normalized not in {"merge", "replace"}:
1457
+ raise ValueError("view_configs[].mode must be merge or replace")
1458
+ self.mode = normalized
1459
+ if normalized == "merge" and "buttons" not in getattr(self, "model_fields_set", set()):
1460
+ raise ValueError("view_configs[] in merge mode requires buttons; pass buttons: [] or mode=replace to clear existing bindings")
1461
+ return self
1462
+
1463
+
1464
+ class CustomButtonsApplyRequest(StrictModel):
1465
+ app_key: str
1466
+ upsert_buttons: list[CustomButtonUpsertPatch] = Field(default_factory=list)
1467
+ patch_buttons: list[CustomButtonPartialPatch] = Field(default_factory=list)
1468
+ remove_buttons: list[CustomButtonRemovePatch] = Field(default_factory=list)
1469
+ view_configs: list[CustomButtonViewConfigPatch] = Field(default_factory=list)
1470
+
1471
+ @model_validator(mode="before")
1472
+ @classmethod
1473
+ def normalize_aliases(cls, value: Any) -> Any:
1474
+ if not isinstance(value, dict):
1475
+ return value
1476
+ payload = dict(value)
1477
+ if "buttons" in payload and "upsert_buttons" not in payload:
1478
+ payload["upsert_buttons"] = payload.pop("buttons")
1479
+ if "upsertButtons" in payload and "upsert_buttons" not in payload:
1480
+ payload["upsert_buttons"] = payload.pop("upsertButtons")
1481
+ if "patchButtons" in payload and "patch_buttons" not in payload:
1482
+ payload["patch_buttons"] = payload.pop("patchButtons")
1483
+ if "removeButtons" in payload and "remove_buttons" not in payload:
1484
+ payload["remove_buttons"] = payload.pop("removeButtons")
1485
+ if "viewConfigs" in payload and "view_configs" not in payload:
1486
+ payload["view_configs"] = payload.pop("viewConfigs")
1487
+ return payload
1488
+
1489
+ @model_validator(mode="after")
1490
+ def validate_shape(self) -> "CustomButtonsApplyRequest":
1491
+ if not self.upsert_buttons and not self.patch_buttons and not self.remove_buttons and not self.view_configs:
1492
+ raise ValueError("custom button apply requires at least one upsert, patch, remove, or view config operation")
1493
+ return self
1494
+
1495
+
1496
+ class AssociatedResourceUpsertPatch(StrictModel):
1497
+ client_key: str | None = Field(default=None, validation_alias=AliasChoices("client_key", "clientKey"))
1498
+ associated_item_id: int | None = Field(
1499
+ default=None,
1500
+ validation_alias=AliasChoices("associated_item_id", "associatedItemId", "asosChartId", "id"),
1501
+ )
1502
+ graph_type: str = Field(validation_alias=AliasChoices("graph_type", "graphType"))
1503
+ target_app_key: str = Field(validation_alias=AliasChoices("target_app_key", "targetAppKey", "app_key", "appKey"))
1504
+ chart_key: str | None = Field(default=None, validation_alias=AliasChoices("chart_key", "chartKey"))
1505
+ view_key: str | None = Field(default=None, validation_alias=AliasChoices("view_key", "viewKey", "viewgraphKey", "viewGraphKey"))
1506
+ report_source: str | None = Field(default=None, validation_alias=AliasChoices("report_source", "reportSource"))
1507
+ source_type: str | None = Field(default=None, validation_alias=AliasChoices("source_type", "sourceType"))
1508
+ match_rules: list[CustomButtonMatchRulePatch] = Field(default_factory=list, validation_alias=AliasChoices("match_rules", "matchRules"))
1509
+ match_mappings: list[FieldMatchMappingPatch] = Field(
1510
+ default_factory=list,
1511
+ validation_alias=AliasChoices("match_mappings", "matchMappings"),
1512
+ )
1513
+
1514
+ @model_validator(mode="before")
1515
+ @classmethod
1516
+ def normalize_aliases(cls, value: Any) -> Any:
1517
+ if not isinstance(value, dict):
1518
+ return value
1519
+ payload = dict(value)
1520
+ payload = _normalize_builder_view_key_payload(payload)
1521
+ if "chart_id" in payload and "chart_key" not in payload and "chartKey" not in payload:
1522
+ payload["chart_key"] = str(payload.pop("chart_id"))
1523
+ raw_graph_type = str(payload.get("graph_type", payload.get("graphType", "")) or "").strip().lower()
1524
+ raw_source_type = payload.get("source_type", payload.get("sourceType"))
1525
+ has_report_source = "report_source" in payload or "reportSource" in payload
1526
+ if isinstance(raw_source_type, str):
1527
+ normalized_source = raw_source_type.strip().upper()
1528
+ if normalized_source == "BI_QINGFLOW" and not has_report_source:
1529
+ payload["report_source"] = "app"
1530
+ payload.pop("source_type", None)
1531
+ payload.pop("sourceType", None)
1532
+ elif normalized_source == "BI_DATASET" and not has_report_source:
1533
+ payload["report_source"] = "dataset"
1534
+ payload.pop("source_type", None)
1535
+ payload.pop("sourceType", None)
1536
+ elif normalized_source == "QINGFLOW" and raw_graph_type in {"view", "viewgraph"}:
1537
+ payload.pop("source_type", None)
1538
+ payload.pop("sourceType", None)
1539
+ return payload
1540
+
1541
+
1542
+ class AssociatedResourcePartialPatch(StrictModel):
1543
+ associated_item_id: int = Field(validation_alias=AliasChoices("associated_item_id", "associatedItemId", "asosChartId", "id"))
1544
+ client_key: str | None = Field(default=None, validation_alias=AliasChoices("client_key", "clientKey"))
1545
+ set: dict[str, Any] = Field(default_factory=dict, validation_alias=AliasChoices("set", "update", "values"))
1546
+ unset: list[str] = Field(default_factory=list, validation_alias=AliasChoices("unset", "clear", "remove"))
1547
+
1548
+ @model_validator(mode="after")
1549
+ def validate_shape(self) -> "AssociatedResourcePartialPatch":
1550
+ if self.associated_item_id <= 0:
1551
+ raise ValueError("patch_resources[].associated_item_id must be a positive integer")
1552
+ if not self.set and not self.unset:
1553
+ raise ValueError("patch_resources[] requires set or unset")
1554
+ return self
1555
+
1556
+
1557
+ class AssociatedResourceViewConfigPatch(StrictModel):
1558
+ view_key: str = Field(validation_alias=AliasChoices("view_key", "viewKey", "viewgraphKey", "viewGraphKey"))
1559
+ visible: bool = Field(default=True, validation_alias=AliasChoices("visible", "enabled", "asosChartVisible"))
1560
+ limit_type: str | None = Field(default=None, validation_alias=AliasChoices("limit_type", "limitType"))
1561
+ associated_item_ids: list[Any] = Field(
1562
+ default_factory=list,
1563
+ validation_alias=AliasChoices("associated_item_ids", "associatedItemIds", "asosChartIdList"),
1564
+ )
1565
+ associated_item_refs: list[str] = Field(default_factory=list, validation_alias=AliasChoices("associated_item_refs", "associatedItemRefs", "refs"))
1566
+
1567
+ @model_validator(mode="before")
1568
+ @classmethod
1569
+ def normalize_aliases(cls, value: Any) -> Any:
1570
+ if not isinstance(value, dict):
1571
+ return value
1572
+ return _normalize_builder_view_key_payload(dict(value))
1573
+
1574
+
1575
+ class AssociatedResourcesApplyRequest(StrictModel):
1576
+ app_key: str
1577
+ upsert_resources: list[AssociatedResourceUpsertPatch] = Field(default_factory=list)
1578
+ patch_resources: list[AssociatedResourcePartialPatch] = Field(default_factory=list)
1579
+ remove_associated_item_ids: list[int] = Field(default_factory=list)
1580
+ reorder_associated_item_ids: list[int] = Field(default_factory=list)
1581
+ view_configs: list[AssociatedResourceViewConfigPatch] = Field(default_factory=list)
1582
+
1583
+ @model_validator(mode="before")
1584
+ @classmethod
1585
+ def normalize_aliases(cls, value: Any) -> Any:
1586
+ if not isinstance(value, dict):
1587
+ return value
1588
+ payload = dict(value)
1589
+ if "upsertResources" in payload and "upsert_resources" not in payload:
1590
+ payload["upsert_resources"] = payload.pop("upsertResources")
1591
+ if "patchResources" in payload and "patch_resources" not in payload:
1592
+ payload["patch_resources"] = payload.pop("patchResources")
1593
+ if "resources" in payload and "upsert_resources" not in payload:
1594
+ payload["upsert_resources"] = payload.pop("resources")
1595
+ if "removeAssociatedItemIds" in payload and "remove_associated_item_ids" not in payload:
1596
+ payload["remove_associated_item_ids"] = payload.pop("removeAssociatedItemIds")
1597
+ if "reorderAssociatedItemIds" in payload and "reorder_associated_item_ids" not in payload:
1598
+ payload["reorder_associated_item_ids"] = payload.pop("reorderAssociatedItemIds")
1599
+ if "viewConfigs" in payload and "view_configs" not in payload:
1600
+ payload["view_configs"] = payload.pop("viewConfigs")
1601
+ return payload
1602
+
1603
+ @model_validator(mode="after")
1604
+ def validate_shape(self) -> "AssociatedResourcesApplyRequest":
1605
+ 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:
1606
+ raise ValueError("associated resources apply requires at least one upsert, patch, remove, reorder, or view config operation")
1607
+ return self
1608
+
1609
+
1244
1610
  class ViewButtonBindingPatch(StrictModel):
1245
1611
  button_type: PublicViewButtonType = Field(validation_alias=AliasChoices("button_type", "buttonType"))
1246
1612
  config_type: PublicViewButtonConfigType = Field(validation_alias=AliasChoices("config_type", "configType"))
@@ -1278,10 +1644,12 @@ class ViewButtonBindingPatch(StrictModel):
1278
1644
  raw_config_type = payload.get("config_type", payload.get("configType"))
1279
1645
  if isinstance(raw_config_type, str):
1280
1646
  normalized_config = raw_config_type.strip().lower()
1281
- if normalized_config == "top":
1647
+ if normalized_config in {"top", "header"}:
1282
1648
  payload["config_type"] = "TOP"
1283
1649
  elif normalized_config == "detail":
1284
1650
  payload["config_type"] = "DETAIL"
1651
+ elif normalized_config in {"inside", "list", "row", "row_action"}:
1652
+ payload["config_type"] = "INSIDE"
1285
1653
  raw_limits = payload.get("button_limit", payload.get("buttonLimit"))
1286
1654
  if isinstance(raw_limits, list) and raw_limits and all(isinstance(item, dict) for item in raw_limits):
1287
1655
  payload["button_limit"] = [raw_limits]
@@ -1423,9 +1791,39 @@ class ChartUpsertPatch(StrictModel):
1423
1791
  return payload
1424
1792
 
1425
1793
 
1794
+ class ChartPartialPatch(StrictModel):
1795
+ chart_id: str | None = None
1796
+ name: str | None = None
1797
+ set: dict[str, Any] = Field(default_factory=dict, validation_alias=AliasChoices("set", "update", "values"))
1798
+ unset: list[str] = Field(default_factory=list, validation_alias=AliasChoices("unset", "clear", "remove"))
1799
+
1800
+ @model_validator(mode="before")
1801
+ @classmethod
1802
+ def normalize_aliases(cls, value: Any) -> Any:
1803
+ if not isinstance(value, dict):
1804
+ return value
1805
+ payload = dict(value)
1806
+ if "id" in payload and "chart_id" not in payload:
1807
+ payload["chart_id"] = str(payload.pop("id"))
1808
+ if "chart_name" in payload and "name" not in payload:
1809
+ payload["name"] = payload.pop("chart_name")
1810
+ if "chartName" in payload and "name" not in payload:
1811
+ payload["name"] = payload.pop("chartName")
1812
+ return payload
1813
+
1814
+ @model_validator(mode="after")
1815
+ def validate_shape(self) -> "ChartPartialPatch":
1816
+ if not str(self.chart_id or "").strip() and not str(self.name or "").strip():
1817
+ raise ValueError("patch_charts[] requires chart_id or unique name")
1818
+ if not self.set and not self.unset:
1819
+ raise ValueError("patch_charts[] requires set or unset")
1820
+ return self
1821
+
1822
+
1426
1823
  class ChartApplyRequest(StrictModel):
1427
1824
  app_key: str
1428
1825
  upsert_charts: list[ChartUpsertPatch] = Field(default_factory=list)
1826
+ patch_charts: list[ChartPartialPatch] = Field(default_factory=list)
1429
1827
  remove_chart_ids: list[str] = Field(default_factory=list)
1430
1828
  reorder_chart_ids: list[str] = Field(default_factory=list)
1431
1829
 
@@ -1435,6 +1833,14 @@ class ChartApplyRequest(StrictModel):
1435
1833
  if not isinstance(value, dict):
1436
1834
  return value
1437
1835
  payload = dict(value)
1836
+ if "upsertCharts" in payload and "upsert_charts" not in payload:
1837
+ payload["upsert_charts"] = payload.pop("upsertCharts")
1838
+ if "patchCharts" in payload and "patch_charts" not in payload:
1839
+ payload["patch_charts"] = payload.pop("patchCharts")
1840
+ if "removeChartIds" in payload and "remove_chart_ids" not in payload:
1841
+ payload["remove_chart_ids"] = payload.pop("removeChartIds")
1842
+ if "reorderChartIds" in payload and "reorder_chart_ids" not in payload:
1843
+ payload["reorder_chart_ids"] = payload.pop("reorderChartIds")
1438
1844
  for key in ("remove_chart_ids", "reorder_chart_ids"):
1439
1845
  raw = payload.get(key)
1440
1846
  if isinstance(raw, list):
@@ -1443,8 +1849,8 @@ class ChartApplyRequest(StrictModel):
1443
1849
 
1444
1850
  @model_validator(mode="after")
1445
1851
  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")
1852
+ if not self.upsert_charts and not self.patch_charts and not self.remove_chart_ids and not self.reorder_chart_ids:
1853
+ raise ValueError("chart apply requires at least one upsert, patch, remove, or reorder operation")
1448
1854
  return self
1449
1855
 
1450
1856
 
@@ -1504,6 +1910,13 @@ class PortalViewRefPatch(StrictModel):
1504
1910
  view_key: str | None = None
1505
1911
  view_name: str | None = None
1506
1912
 
1913
+ @model_validator(mode="before")
1914
+ @classmethod
1915
+ def normalize_aliases(cls, value: Any) -> Any:
1916
+ if not isinstance(value, dict):
1917
+ return value
1918
+ return _normalize_builder_view_key_payload(dict(value))
1919
+
1507
1920
  @model_validator(mode="after")
1508
1921
  def validate_target(self) -> "PortalViewRefPatch":
1509
1922
  if not (self.view_key or self.view_name):
@@ -1597,15 +2010,25 @@ class AppGetResponse(StrictModel):
1597
2010
  field_count: int = 0
1598
2011
  layout_section_count: int = 0
1599
2012
  view_count: int = 0
2013
+ chart_count: int = 0
2014
+ associated_resource_count: int = 0
2015
+ custom_button_count: int = 0
1600
2016
  workflow_enabled: bool = False
2017
+ counts: dict[str, int] = Field(default_factory=dict)
2018
+ views: list[dict[str, Any]] = Field(default_factory=list)
2019
+ charts: list[dict[str, Any]] = Field(default_factory=list)
2020
+ associated_resources: list[dict[str, Any]] = Field(default_factory=list)
2021
+ custom_buttons: list[dict[str, Any]] = Field(default_factory=list)
1601
2022
  verification_hints: list[str] = Field(default_factory=list)
1602
2023
  editability: dict[str, bool | None] = Field(default_factory=dict)
2024
+ form_settings: dict[str, Any] = Field(default_factory=dict)
1603
2025
 
1604
2026
 
1605
2027
  class AppGetFieldsResponse(StrictModel):
1606
2028
  app_key: str
1607
2029
  fields: list[dict[str, Any]] = Field(default_factory=list)
1608
2030
  field_count: int = 0
2031
+ form_settings: dict[str, Any] = Field(default_factory=dict)
1609
2032
 
1610
2033
 
1611
2034
  class AppGetLayoutResponse(StrictModel):
@@ -1681,6 +2104,8 @@ class ViewGetResponse(StrictModel):
1681
2104
  config: dict[str, Any] = Field(default_factory=dict)
1682
2105
  questions: list[dict[str, Any]] = Field(default_factory=list)
1683
2106
  associations: list[dict[str, Any]] = Field(default_factory=list)
2107
+ buttons_config: dict[str, Any] = Field(default_factory=dict)
2108
+ associated_resources_config: dict[str, Any] = Field(default_factory=dict)
1684
2109
 
1685
2110
 
1686
2111
  class ChartGetResponse(StrictModel):
@@ -1757,6 +2182,7 @@ class FlowPlanRequest(StrictModel):
1757
2182
  class ViewsPlanRequest(StrictModel):
1758
2183
  app_key: str
1759
2184
  upsert_views: list[ViewUpsertPatch] = Field(default_factory=list)
2185
+ patch_views: list[ViewPartialPatch] = Field(default_factory=list)
1760
2186
  remove_views: list[str] = Field(default_factory=list)
1761
2187
  preset: ViewsPreset | None = None
1762
2188
 
@@ -1879,7 +2305,10 @@ def _normalize_public_department_scope_mode(value: Any) -> str | None:
1879
2305
 
1880
2306
 
1881
2307
  CustomButtonMatchRulePatch.model_rebuild()
2308
+ CustomButtonFieldMappingPatch.model_rebuild()
1882
2309
  CustomButtonAddDataConfigPatch.model_rebuild()
2310
+ CustomButtonViewButtonBindingPatch.model_rebuild()
2311
+ CustomButtonViewConfigPatch.model_rebuild()
1883
2312
  CodeBlockAliasPathPatch.model_rebuild()
1884
2313
  ViewButtonBindingPatch.model_rebuild()
1885
2314
  ViewUpsertPatch.model_rebuild()