@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.
- package/README.md +2 -2
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/skills/qingflow-app-builder/SKILL.md +98 -10
- package/skills/qingflow-app-builder/references/create-app.md +4 -1
- package/skills/qingflow-app-builder/references/gotchas.md +35 -1
- package/skills/qingflow-app-builder/references/match-rules.md +114 -0
- package/skills/qingflow-app-builder/references/tool-selection.md +9 -5
- package/skills/qingflow-app-builder/references/update-schema.md +6 -2
- package/skills/qingflow-app-builder/references/update-views.md +126 -5
- package/src/qingflow_mcp/builder_facade/models.py +433 -4
- package/src/qingflow_mcp/builder_facade/service.py +6536 -1212
- package/src/qingflow_mcp/cli/commands/builder.py +47 -26
- package/src/qingflow_mcp/cli/commands/record.py +58 -4
- package/src/qingflow_mcp/public_surface.py +4 -7
- package/src/qingflow_mcp/response_trim.py +17 -6
- package/src/qingflow_mcp/server.py +1 -0
- package/src/qingflow_mcp/server_app_builder.py +43 -20
- package/src/qingflow_mcp/server_app_user.py +1 -0
- package/src/qingflow_mcp/tools/ai_builder_tools.py +579 -119
- package/src/qingflow_mcp/tools/record_tools.py +9 -2
|
@@ -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(
|
|
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
|
|
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()
|