@josephyan/qingflow-cli 1.1.9 → 1.1.11
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
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Install:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install @josephyan/qingflow-cli@1.1.
|
|
6
|
+
npm install @josephyan/qingflow-cli@1.1.11
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @josephyan/qingflow-cli@1.1.
|
|
12
|
+
npx -y -p @josephyan/qingflow-cli@1.1.11 qingflow
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -6079,6 +6079,21 @@ class AiBuilderFacade:
|
|
|
6079
6079
|
details={"app_key": app_key},
|
|
6080
6080
|
suggested_next_call={"tool_name": "app_get", "arguments": {"profile": profile, "app_key": app_key}},
|
|
6081
6081
|
)
|
|
6082
|
+
match_mapping_errors = self._enrich_associated_resource_match_mappings(
|
|
6083
|
+
profile=profile,
|
|
6084
|
+
app_key=app_key,
|
|
6085
|
+
resources=resources,
|
|
6086
|
+
)
|
|
6087
|
+
if match_mapping_errors:
|
|
6088
|
+
verification["match_mappings_loaded"] = False
|
|
6089
|
+
warnings.append(
|
|
6090
|
+
_warning(
|
|
6091
|
+
"ASSOCIATED_RESOURCE_MATCH_MAPPINGS_PARTIAL",
|
|
6092
|
+
"associated resources were read, but some raw match rules could not be converted to semantic match_mappings",
|
|
6093
|
+
)
|
|
6094
|
+
)
|
|
6095
|
+
else:
|
|
6096
|
+
verification["match_mappings_loaded"] = True
|
|
6082
6097
|
view_configs: list[dict[str, Any]] = []
|
|
6083
6098
|
view_config_read_errors: list[dict[str, Any]] = []
|
|
6084
6099
|
try:
|
|
@@ -6111,7 +6126,10 @@ class AiBuilderFacade:
|
|
|
6111
6126
|
"view_config_count": len(view_configs),
|
|
6112
6127
|
"warnings": warnings,
|
|
6113
6128
|
"verification": verification,
|
|
6114
|
-
"details": {
|
|
6129
|
+
"details": {
|
|
6130
|
+
**({"view_config_read_errors": view_config_read_errors} if view_config_read_errors else {}),
|
|
6131
|
+
**({"match_mapping_errors": match_mapping_errors} if match_mapping_errors else {}),
|
|
6132
|
+
},
|
|
6115
6133
|
}
|
|
6116
6134
|
|
|
6117
6135
|
def flow_patch_nodes(
|
|
@@ -6777,6 +6795,64 @@ class AiBuilderFacade:
|
|
|
6777
6795
|
|
|
6778
6796
|
return self.apps._run(profile, runner)
|
|
6779
6797
|
|
|
6798
|
+
def _match_field_index_for_app(self, *, profile: str, app_key: str) -> dict[int, dict[str, Any]]:
|
|
6799
|
+
state = self._load_base_schema_state(profile=profile, app_key=app_key)
|
|
6800
|
+
fields = cast(list[dict[str, Any]], state["parsed"]["fields"])
|
|
6801
|
+
indexed: dict[int, dict[str, Any]] = {
|
|
6802
|
+
-17: {"name": "数据ID", "que_id": -17, "type": "system"},
|
|
6803
|
+
0: {"name": "编号", "que_id": 0, "type": "system"},
|
|
6804
|
+
}
|
|
6805
|
+
for field in fields:
|
|
6806
|
+
que_id = _coerce_any_int(field.get("que_id"))
|
|
6807
|
+
if que_id is not None:
|
|
6808
|
+
indexed[que_id] = field
|
|
6809
|
+
return indexed
|
|
6810
|
+
|
|
6811
|
+
def _enrich_associated_resource_match_mappings(
|
|
6812
|
+
self,
|
|
6813
|
+
*,
|
|
6814
|
+
profile: str,
|
|
6815
|
+
app_key: str,
|
|
6816
|
+
resources: list[dict[str, Any]],
|
|
6817
|
+
) -> list[dict[str, Any]]:
|
|
6818
|
+
if not any(isinstance(resource, dict) and resource.get("match_rules") for resource in resources):
|
|
6819
|
+
return []
|
|
6820
|
+
errors: list[dict[str, Any]] = []
|
|
6821
|
+
source_fields: dict[int, dict[str, Any]] = {}
|
|
6822
|
+
try:
|
|
6823
|
+
source_fields = self._match_field_index_for_app(profile=profile, app_key=app_key)
|
|
6824
|
+
except (QingflowApiError, RuntimeError) as error:
|
|
6825
|
+
errors.append({"app_key": app_key, "resource": "source_fields", "transport_error": _transport_error_payload(_coerce_api_error(error))})
|
|
6826
|
+
target_fields_by_app: dict[str, dict[int, dict[str, Any]]] = {}
|
|
6827
|
+
for resource in resources:
|
|
6828
|
+
if not isinstance(resource, dict):
|
|
6829
|
+
continue
|
|
6830
|
+
raw_rules = resource.get("match_rules")
|
|
6831
|
+
if not raw_rules:
|
|
6832
|
+
continue
|
|
6833
|
+
target_app_key = str(resource.get("target_app_key") or app_key).strip()
|
|
6834
|
+
if target_app_key not in target_fields_by_app:
|
|
6835
|
+
try:
|
|
6836
|
+
target_fields_by_app[target_app_key] = self._match_field_index_for_app(profile=profile, app_key=target_app_key)
|
|
6837
|
+
except (QingflowApiError, RuntimeError) as error:
|
|
6838
|
+
errors.append(
|
|
6839
|
+
{
|
|
6840
|
+
"associated_item_id": resource.get("associated_item_id"),
|
|
6841
|
+
"target_app_key": target_app_key,
|
|
6842
|
+
"resource": "target_fields",
|
|
6843
|
+
"transport_error": _transport_error_payload(_coerce_api_error(error)),
|
|
6844
|
+
}
|
|
6845
|
+
)
|
|
6846
|
+
target_fields_by_app[target_app_key] = {}
|
|
6847
|
+
mappings = _public_associated_resource_match_mappings_from_rules(
|
|
6848
|
+
raw_rules if isinstance(raw_rules, list) else [],
|
|
6849
|
+
source_fields=source_fields,
|
|
6850
|
+
target_fields=target_fields_by_app.get(target_app_key, {}),
|
|
6851
|
+
)
|
|
6852
|
+
if mappings:
|
|
6853
|
+
resource["match_mappings"] = mappings
|
|
6854
|
+
return errors
|
|
6855
|
+
|
|
6780
6856
|
def _associated_resource_create(
|
|
6781
6857
|
self,
|
|
6782
6858
|
*,
|
|
@@ -25139,12 +25215,83 @@ def _normalize_associated_resource_item(raw_item: Any, *, include_raw: bool = Fa
|
|
|
25139
25215
|
if chart_type is not None:
|
|
25140
25216
|
item["chart_type"] = _public_chart_type_from_backend(chart_type)
|
|
25141
25217
|
item["report_source"] = _public_report_source_from_backend_source(source_type)
|
|
25218
|
+
match_rules = _normalize_associated_resource_raw_match_rules(raw_item)
|
|
25219
|
+
if match_rules:
|
|
25220
|
+
item["match_rules"] = match_rules
|
|
25142
25221
|
item = _compact_dict(item)
|
|
25143
25222
|
if include_raw:
|
|
25144
25223
|
item["_raw"] = deepcopy(raw_item)
|
|
25145
25224
|
return item
|
|
25146
25225
|
|
|
25147
25226
|
|
|
25227
|
+
def _normalize_associated_resource_raw_match_rules(raw_item: dict[str, Any]) -> list[dict[str, Any]]:
|
|
25228
|
+
raw_match_rules = _first_present(raw_item, "match_rules", "matchRules", "que_relation", "queRelation")
|
|
25229
|
+
if not isinstance(raw_match_rules, list):
|
|
25230
|
+
return []
|
|
25231
|
+
flattened: list[dict[str, Any]] = []
|
|
25232
|
+
if raw_match_rules and all(isinstance(group, list) for group in raw_match_rules):
|
|
25233
|
+
for group in raw_match_rules:
|
|
25234
|
+
flattened.extend(item for item in group if isinstance(item, dict))
|
|
25235
|
+
else:
|
|
25236
|
+
flattened.extend(item for item in raw_match_rules if isinstance(item, dict))
|
|
25237
|
+
return [_normalize_custom_button_match_rule_for_public(rule) for rule in flattened]
|
|
25238
|
+
|
|
25239
|
+
|
|
25240
|
+
def _match_field_name(field_id: Any, *, fields_by_que_id: dict[int, dict[str, Any]], fallback: Any = None) -> str:
|
|
25241
|
+
normalized_id = _coerce_any_int(field_id)
|
|
25242
|
+
if normalized_id is not None:
|
|
25243
|
+
field = fields_by_que_id.get(normalized_id) or {}
|
|
25244
|
+
name = str(field.get("name") or field.get("title") or "").strip()
|
|
25245
|
+
if name:
|
|
25246
|
+
return name
|
|
25247
|
+
if normalized_id == -17:
|
|
25248
|
+
return "数据ID"
|
|
25249
|
+
if normalized_id == 0:
|
|
25250
|
+
return "编号"
|
|
25251
|
+
fallback_text = str(fallback or "").strip()
|
|
25252
|
+
return fallback_text or str(field_id or "").strip()
|
|
25253
|
+
|
|
25254
|
+
|
|
25255
|
+
def _public_associated_resource_match_mappings_from_rules(
|
|
25256
|
+
rules: list[dict[str, Any]],
|
|
25257
|
+
*,
|
|
25258
|
+
source_fields: dict[int, dict[str, Any]],
|
|
25259
|
+
target_fields: dict[int, dict[str, Any]],
|
|
25260
|
+
) -> list[dict[str, Any]]:
|
|
25261
|
+
mappings: list[dict[str, Any]] = []
|
|
25262
|
+
for rule in rules:
|
|
25263
|
+
if not isinstance(rule, dict):
|
|
25264
|
+
continue
|
|
25265
|
+
target_field_id = _first_present(rule, "que_id", "queId")
|
|
25266
|
+
target_field = target_fields.get(_coerce_any_int(target_field_id) or 0) or {}
|
|
25267
|
+
target_name = _match_field_name(target_field_id, fields_by_que_id=target_fields, fallback=rule.get("que_title"))
|
|
25268
|
+
operator = _public_view_filter_operator_from_judge_type(rule.get("judge_type"))
|
|
25269
|
+
mapping: dict[str, Any] = {
|
|
25270
|
+
"target_field": target_name,
|
|
25271
|
+
"operator": operator,
|
|
25272
|
+
}
|
|
25273
|
+
if _coerce_any_int(rule.get("match_type")) == MATCH_TYPE_QUESTION:
|
|
25274
|
+
source_field_id = _first_present(rule, "judge_que_id", "judgeQueId")
|
|
25275
|
+
source_detail = rule.get("judge_que_detail") if isinstance(rule.get("judge_que_detail"), dict) else {}
|
|
25276
|
+
mapping["source_field"] = _match_field_name(
|
|
25277
|
+
source_field_id,
|
|
25278
|
+
fields_by_que_id=source_fields,
|
|
25279
|
+
fallback=source_detail.get("que_title"),
|
|
25280
|
+
)
|
|
25281
|
+
else:
|
|
25282
|
+
value_rule = {
|
|
25283
|
+
"judgeValues": rule.get("judge_values") or [],
|
|
25284
|
+
"judgeValueDetails": rule.get("judge_value_details") or [],
|
|
25285
|
+
}
|
|
25286
|
+
values = _public_view_filter_rule_values(value_rule, field=target_field)
|
|
25287
|
+
if len(values) == 1:
|
|
25288
|
+
mapping["value"] = values[0]
|
|
25289
|
+
elif values:
|
|
25290
|
+
mapping["values"] = values
|
|
25291
|
+
mappings.append(_compact_dict(mapping))
|
|
25292
|
+
return mappings
|
|
25293
|
+
|
|
25294
|
+
|
|
25148
25295
|
def _normalize_associated_graph_type(raw_item: dict[str, Any], *, chart_key: Any, view_key: Any) -> str:
|
|
25149
25296
|
raw_graph_type = str(_first_present(raw_item, "graph_type", "graphType", "resourceType", "type") or "").strip().lower()
|
|
25150
25297
|
raw_source_type = str(_first_present(raw_item, "source_type", "sourceType") or "").strip().lower()
|
|
@@ -898,7 +898,11 @@ def _handle_portal_apply(args: argparse.Namespace, context: CliContext) -> dict:
|
|
|
898
898
|
fix_hint="Use `--dash-key` for an existing portal. For create mode, pass `--package-id --dash-name`.",
|
|
899
899
|
)
|
|
900
900
|
sections = [] if not args.sections_file else require_list_arg(args.sections_file, option_name="--sections-file")
|
|
901
|
-
patch_sections =
|
|
901
|
+
patch_sections = (
|
|
902
|
+
load_list_arg(args.patch_sections_file, option_name="--patch-sections-file")
|
|
903
|
+
if args.patch_sections_file
|
|
904
|
+
else None
|
|
905
|
+
)
|
|
902
906
|
return context.builder.portal_apply(
|
|
903
907
|
profile=args.profile,
|
|
904
908
|
dash_key=args.dash_key,
|