@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.
- package/README.md +3 -3
- package/npm/bin/qingflow.mjs +40 -2
- package/npm/lib/runtime.mjs +386 -15
- package/npm/scripts/postinstall.mjs +7 -2
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/skills/qingflow-cli/SKILL.md +440 -0
- package/skills/qingflow-cli/manifest.yaml +10 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_ADMIN_CHEATSHEET.md +94 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md +485 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_CHARTS_WORKFLOW.md +237 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_MATCH_RULES.md +137 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_PORTAL_WORKFLOW.md +263 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md +304 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_WORKSPACE_ICONS.md +41 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md +139 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_EXPLORATION_REPORT.md +84 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_FIELD_DATA_TYPES.md +129 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_MEMBER_CHEATSHEET.md +195 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md +159 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md +20 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md +176 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md +163 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_SCHEMA_APPLY_FIELD_TYPES_AND_SCENARIOS.md +107 -0
- package/skills/qingflow-cli/reference/QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md +151 -0
- package/skills/qingflow-cli/reference/_batch_schema_complex.json +18 -0
- package/skills/qingflow-cli/reference/_batch_schema_scalar.json +17 -0
- package/skills/qingflow-cli/reference/charts_remove.example.json +1 -0
- package/skills/qingflow-cli/reference/charts_reorder.example.json +1 -0
- package/skills/qingflow-cli/reference/charts_upsert_bar.example.json +8 -0
- package/skills/qingflow-cli/reference/charts_upsert_dashboard_starter.example.json +37 -0
- package/skills/qingflow-cli/reference/charts_upsert_minimal.example.json +13 -0
- package/skills/qingflow-cli/reference/portal_sections_all_types.example.json +131 -0
- package/skills/qingflow-cli/reference/portal_sections_five_types.example.json +126 -0
- package/skills/qingflow-cli/reference/portal_sections_standard_workbench.example.json +128 -0
- package/skills/qingflow-cli/reference/schema_add_fields_minimal.example.json +7 -0
- package/skills/qingflow-cli/reference/schema_apply_add_fields_all_types.json +78 -0
- package/skills/qingflow-cli/reference/views_upsert_table_minimal.example.json +7 -0
- package/skills/qingflow-cli/scripts/builder-package-from-app-list.py +140 -0
- package/skills/qingflow-cli/scripts/find-app-by-keyword.py +132 -0
- package/skills/qingflow-cli/scripts/validate_qingflow_output_files.py +87 -0
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/builder_facade/models.py +532 -48
- package/src/qingflow_mcp/builder_facade/service.py +9194 -2384
- package/src/qingflow_mcp/builder_facade/workflow_spec.py +111 -0
- package/src/qingflow_mcp/cli/commands/app.py +3 -16
- package/src/qingflow_mcp/cli/commands/builder.py +354 -56
- package/src/qingflow_mcp/cli/commands/record.py +89 -2
- package/src/qingflow_mcp/cli/formatters.py +32 -1
- package/src/qingflow_mcp/cli/main.py +245 -3
- package/src/qingflow_mcp/public_surface.py +11 -8
- package/src/qingflow_mcp/response_trim.py +143 -14
- package/src/qingflow_mcp/server.py +15 -12
- package/src/qingflow_mcp/server_app_builder.py +108 -30
- package/src/qingflow_mcp/server_app_user.py +17 -18
- package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
- package/src/qingflow_mcp/solution/compiler/icon_utils.py +294 -0
- package/src/qingflow_mcp/solution/executor.py +3 -133
- package/src/qingflow_mcp/tools/ai_builder_tools.py +2617 -440
- package/src/qingflow_mcp/tools/app_tools.py +53 -8
- package/src/qingflow_mcp/tools/package_tools.py +16 -2
- package/src/qingflow_mcp/tools/record_tools.py +2095 -176
- package/src/qingflow_mcp/tools/resource_read_tools.py +3 -0
- package/src/qingflow_mcp/tools/solution_tools.py +30 -2
- package/src/qingflow_mcp/tools/workflow_tools.py +3 -31
- package/src/qingflow_mcp/version.py +110 -0
- 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(
|
|
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
|
|
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=
|
|
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()
|