@josephyan/qingflow-cli 0.2.0-beta.67 → 0.2.0-beta.68
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/src/qingflow_mcp/builder_facade/models.py +348 -2
- package/src/qingflow_mcp/builder_facade/service.py +2006 -149
- package/src/qingflow_mcp/cli/commands/record.py +2 -0
- package/src/qingflow_mcp/server.py +2 -1
- package/src/qingflow_mcp/server_app_user.py +2 -1
- package/src/qingflow_mcp/solution/compiler/form_compiler.py +29 -0
- package/src/qingflow_mcp/solution/spec_models.py +2 -0
- package/src/qingflow_mcp/tools/ai_builder_tools.py +99 -10
- package/src/qingflow_mcp/tools/approval_tools.py +31 -2
- package/src/qingflow_mcp/tools/code_block_tools.py +91 -14
- package/src/qingflow_mcp/tools/record_tools.py +549 -185
- package/src/qingflow_mcp/tools/task_context_tools.py +26 -1
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Install:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install @josephyan/qingflow-cli@0.2.0-beta.
|
|
6
|
+
npm install @josephyan/qingflow-cli@0.2.0-beta.68
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @josephyan/qingflow-cli@0.2.0-beta.
|
|
12
|
+
npx -y -p @josephyan/qingflow-cli@0.2.0-beta.68 qingflow
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@josephyan/qingflow-cli",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.68",
|
|
4
4
|
"description": "Human-friendly Qingflow command line interface for auth, record operations, import, tasks, and stable builder flows.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/pyproject.toml
CHANGED
|
@@ -24,6 +24,8 @@ class PublicFieldType(str, Enum):
|
|
|
24
24
|
address = "address"
|
|
25
25
|
attachment = "attachment"
|
|
26
26
|
boolean = "boolean"
|
|
27
|
+
q_linker = "q_linker"
|
|
28
|
+
code_block = "code_block"
|
|
27
29
|
relation = "relation"
|
|
28
30
|
subtable = "subtable"
|
|
29
31
|
|
|
@@ -46,6 +48,10 @@ FIELD_TYPE_ALIASES: dict[str, PublicFieldType] = {
|
|
|
46
48
|
"multi_select": PublicFieldType.multi_select,
|
|
47
49
|
"multi-select": PublicFieldType.multi_select,
|
|
48
50
|
"departments": PublicFieldType.department,
|
|
51
|
+
"qlinker": PublicFieldType.q_linker,
|
|
52
|
+
"q_linker": PublicFieldType.q_linker,
|
|
53
|
+
"codeblock": PublicFieldType.code_block,
|
|
54
|
+
"code_block": PublicFieldType.code_block,
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
FIELD_TYPE_ID_ALIASES: dict[int, PublicFieldType] = {
|
|
@@ -60,6 +66,8 @@ FIELD_TYPE_ID_ALIASES: dict[int, PublicFieldType] = {
|
|
|
60
66
|
11: PublicFieldType.single_select,
|
|
61
67
|
12: PublicFieldType.multi_select,
|
|
62
68
|
13: PublicFieldType.attachment,
|
|
69
|
+
20: PublicFieldType.q_linker,
|
|
70
|
+
26: PublicFieldType.code_block,
|
|
63
71
|
18: PublicFieldType.subtable,
|
|
64
72
|
21: PublicFieldType.address,
|
|
65
73
|
22: PublicFieldType.department,
|
|
@@ -281,6 +289,260 @@ class FieldSelector(StrictModel):
|
|
|
281
289
|
return self
|
|
282
290
|
|
|
283
291
|
|
|
292
|
+
class CodeBlockAliasPathPatch(StrictModel):
|
|
293
|
+
alias_name: str = Field(validation_alias=AliasChoices("alias_name", "aliasName"))
|
|
294
|
+
alias_path: str = Field(validation_alias=AliasChoices("alias_path", "aliasPath"))
|
|
295
|
+
alias_type: int = Field(default=1, validation_alias=AliasChoices("alias_type", "aliasType"))
|
|
296
|
+
alias_id: int | None = Field(default=None, validation_alias=AliasChoices("alias_id", "aliasId"))
|
|
297
|
+
sub_alias: list["CodeBlockAliasPathPatch"] = Field(
|
|
298
|
+
default_factory=list,
|
|
299
|
+
validation_alias=AliasChoices("sub_alias", "subAlias"),
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
class CodeBlockConfigPatch(StrictModel):
|
|
304
|
+
config_mode: int = Field(default=1, validation_alias=AliasChoices("config_mode", "configMode"))
|
|
305
|
+
code_content: str = Field(default="", validation_alias=AliasChoices("code_content", "codeContent"))
|
|
306
|
+
being_hide_on_form: bool = Field(
|
|
307
|
+
default=False,
|
|
308
|
+
validation_alias=AliasChoices("being_hide_on_form", "beingHideOnForm"),
|
|
309
|
+
)
|
|
310
|
+
result_alias_path: list[CodeBlockAliasPathPatch] = Field(
|
|
311
|
+
default_factory=list,
|
|
312
|
+
validation_alias=AliasChoices("result_alias_path", "resultAliasPath", "alias_config", "aliasConfig"),
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
class CodeBlockInputBindingPatch(StrictModel):
|
|
317
|
+
field: FieldSelector
|
|
318
|
+
var: str | None = None
|
|
319
|
+
|
|
320
|
+
@model_validator(mode="before")
|
|
321
|
+
@classmethod
|
|
322
|
+
def normalize_aliases(cls, value: Any) -> Any:
|
|
323
|
+
if isinstance(value, str):
|
|
324
|
+
return {"field": {"name": value}}
|
|
325
|
+
if not isinstance(value, dict):
|
|
326
|
+
return value
|
|
327
|
+
payload = dict(value)
|
|
328
|
+
raw_field = payload.get("field")
|
|
329
|
+
if isinstance(raw_field, str):
|
|
330
|
+
payload["field"] = {"name": raw_field}
|
|
331
|
+
elif raw_field is None:
|
|
332
|
+
for key in ("field_name", "fieldName", "name", "title", "label"):
|
|
333
|
+
raw_value = payload.get(key)
|
|
334
|
+
if isinstance(raw_value, str) and raw_value.strip():
|
|
335
|
+
payload["field"] = {"name": raw_value}
|
|
336
|
+
break
|
|
337
|
+
return payload
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
class QLinkerInputSource(str, Enum):
|
|
341
|
+
query_param = "query_param"
|
|
342
|
+
header = "header"
|
|
343
|
+
url_encoded = "url_encoded"
|
|
344
|
+
json_path = "json_path"
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
class QLinkerKeyValuePatch(StrictModel):
|
|
348
|
+
key: str
|
|
349
|
+
value: str | None = None
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
class QLinkerAliasPathPatch(StrictModel):
|
|
353
|
+
alias_name: str = Field(validation_alias=AliasChoices("alias_name", "aliasName"))
|
|
354
|
+
alias_path: str = Field(validation_alias=AliasChoices("alias_path", "aliasPath"))
|
|
355
|
+
alias_id: int | None = Field(default=None, validation_alias=AliasChoices("alias_id", "aliasId"))
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
class RemoteLookupConfigPatch(StrictModel):
|
|
359
|
+
config_mode: int = Field(default=1, validation_alias=AliasChoices("config_mode", "configMode"))
|
|
360
|
+
url: str = ""
|
|
361
|
+
method: str = "GET"
|
|
362
|
+
headers: list[QLinkerKeyValuePatch] = Field(default_factory=list)
|
|
363
|
+
body_type: int = Field(default=1, validation_alias=AliasChoices("body_type", "bodyType"))
|
|
364
|
+
url_encoded_value: list[QLinkerKeyValuePatch] = Field(
|
|
365
|
+
default_factory=list,
|
|
366
|
+
validation_alias=AliasChoices("url_encoded_value", "urlEncodedValue"),
|
|
367
|
+
)
|
|
368
|
+
json_value: str | None = Field(default=None, validation_alias=AliasChoices("json_value", "jsonValue"))
|
|
369
|
+
xml_value: str | None = Field(default=None, validation_alias=AliasChoices("xml_value", "xmlValue"))
|
|
370
|
+
result_type: int = Field(default=1, validation_alias=AliasChoices("result_type", "resultType"))
|
|
371
|
+
result_format_path: list[QLinkerAliasPathPatch] = Field(
|
|
372
|
+
default_factory=list,
|
|
373
|
+
validation_alias=AliasChoices("result_format_path", "resultFormatPath"),
|
|
374
|
+
)
|
|
375
|
+
query_params: list[QLinkerKeyValuePatch] = Field(
|
|
376
|
+
default_factory=list,
|
|
377
|
+
validation_alias=AliasChoices("query_params", "queryParams"),
|
|
378
|
+
)
|
|
379
|
+
auto_trigger: bool | None = Field(default=None, validation_alias=AliasChoices("auto_trigger", "autoTrigger"))
|
|
380
|
+
custom_button_text_enabled: bool | None = Field(
|
|
381
|
+
default=None,
|
|
382
|
+
validation_alias=AliasChoices("custom_button_text_enabled", "customButtonTextEnabled", "custom_btn_text_status", "customBtnTextStatus"),
|
|
383
|
+
)
|
|
384
|
+
custom_button_text: str | None = Field(
|
|
385
|
+
default=None,
|
|
386
|
+
validation_alias=AliasChoices("custom_button_text", "customButtonText", "custom_btn_text", "customBtnText"),
|
|
387
|
+
)
|
|
388
|
+
being_insert_value_directly: bool | None = Field(
|
|
389
|
+
default=None,
|
|
390
|
+
validation_alias=AliasChoices("being_insert_value_directly", "beingInsertValueDirectly"),
|
|
391
|
+
)
|
|
392
|
+
being_hide_on_form: bool | None = Field(
|
|
393
|
+
default=None,
|
|
394
|
+
validation_alias=AliasChoices("being_hide_on_form", "beingHideOnForm"),
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
class QLinkerInputBindingPatch(StrictModel):
|
|
399
|
+
field: FieldSelector
|
|
400
|
+
key: str
|
|
401
|
+
source: QLinkerInputSource
|
|
402
|
+
|
|
403
|
+
@model_validator(mode="before")
|
|
404
|
+
@classmethod
|
|
405
|
+
def normalize_aliases(cls, value: Any) -> Any:
|
|
406
|
+
if isinstance(value, str):
|
|
407
|
+
return {"field": {"name": value}, "key": value, "source": QLinkerInputSource.query_param.value}
|
|
408
|
+
if not isinstance(value, dict):
|
|
409
|
+
return value
|
|
410
|
+
payload = dict(value)
|
|
411
|
+
raw_field = payload.get("field")
|
|
412
|
+
if isinstance(raw_field, str):
|
|
413
|
+
payload["field"] = {"name": raw_field}
|
|
414
|
+
elif raw_field is None:
|
|
415
|
+
for key in ("field_name", "fieldName", "name", "title", "label"):
|
|
416
|
+
raw_value = payload.get(key)
|
|
417
|
+
if isinstance(raw_value, str) and raw_value.strip():
|
|
418
|
+
payload["field"] = {"name": raw_value}
|
|
419
|
+
break
|
|
420
|
+
raw_source = payload.get("source")
|
|
421
|
+
if isinstance(raw_source, str):
|
|
422
|
+
payload["source"] = raw_source.strip().lower()
|
|
423
|
+
return payload
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
class QLinkerOutputBindingPatch(StrictModel):
|
|
427
|
+
alias: str
|
|
428
|
+
path: str
|
|
429
|
+
target_field: FieldSelector = Field(validation_alias=AliasChoices("target_field", "targetField"))
|
|
430
|
+
|
|
431
|
+
@model_validator(mode="before")
|
|
432
|
+
@classmethod
|
|
433
|
+
def normalize_aliases(cls, value: Any) -> Any:
|
|
434
|
+
if not isinstance(value, dict):
|
|
435
|
+
return value
|
|
436
|
+
payload = dict(value)
|
|
437
|
+
if "alias_name" in payload and "alias" not in payload:
|
|
438
|
+
payload["alias"] = payload.pop("alias_name")
|
|
439
|
+
if "aliasName" in payload and "alias" not in payload:
|
|
440
|
+
payload["alias"] = payload.pop("aliasName")
|
|
441
|
+
if "alias_path" in payload and "path" not in payload:
|
|
442
|
+
payload["path"] = payload.pop("alias_path")
|
|
443
|
+
if "aliasPath" in payload and "path" not in payload:
|
|
444
|
+
payload["path"] = payload.pop("aliasPath")
|
|
445
|
+
raw_target = payload.get("target_field", payload.get("targetField"))
|
|
446
|
+
if isinstance(raw_target, str):
|
|
447
|
+
payload["target_field"] = {"name": raw_target}
|
|
448
|
+
return payload
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
class QLinkerRequestPatch(StrictModel):
|
|
452
|
+
url: str = ""
|
|
453
|
+
method: str = "GET"
|
|
454
|
+
headers: list[QLinkerKeyValuePatch] = Field(default_factory=list)
|
|
455
|
+
query_params: list[QLinkerKeyValuePatch] = Field(
|
|
456
|
+
default_factory=list,
|
|
457
|
+
validation_alias=AliasChoices("query_params", "queryParams"),
|
|
458
|
+
)
|
|
459
|
+
body_type: int = Field(default=1, validation_alias=AliasChoices("body_type", "bodyType"))
|
|
460
|
+
url_encoded_value: list[QLinkerKeyValuePatch] = Field(
|
|
461
|
+
default_factory=list,
|
|
462
|
+
validation_alias=AliasChoices("url_encoded_value", "urlEncodedValue"),
|
|
463
|
+
)
|
|
464
|
+
json_value: str | None = Field(default=None, validation_alias=AliasChoices("json_value", "jsonValue"))
|
|
465
|
+
xml_value: str | None = Field(default=None, validation_alias=AliasChoices("xml_value", "xmlValue"))
|
|
466
|
+
result_type: int = Field(default=1, validation_alias=AliasChoices("result_type", "resultType"))
|
|
467
|
+
auto_trigger: bool | None = Field(default=None, validation_alias=AliasChoices("auto_trigger", "autoTrigger"))
|
|
468
|
+
custom_button_text_enabled: bool | None = Field(
|
|
469
|
+
default=None,
|
|
470
|
+
validation_alias=AliasChoices("custom_button_text_enabled", "customButtonTextEnabled", "custom_btn_text_status", "customBtnTextStatus"),
|
|
471
|
+
)
|
|
472
|
+
custom_button_text: str | None = Field(
|
|
473
|
+
default=None,
|
|
474
|
+
validation_alias=AliasChoices("custom_button_text", "customButtonText", "custom_btn_text", "customBtnText"),
|
|
475
|
+
)
|
|
476
|
+
being_insert_value_directly: bool | None = Field(
|
|
477
|
+
default=None,
|
|
478
|
+
validation_alias=AliasChoices("being_insert_value_directly", "beingInsertValueDirectly"),
|
|
479
|
+
)
|
|
480
|
+
being_hide_on_form: bool | None = Field(
|
|
481
|
+
default=None,
|
|
482
|
+
validation_alias=AliasChoices("being_hide_on_form", "beingHideOnForm"),
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
class QLinkerBindingPatch(StrictModel):
|
|
487
|
+
inputs: list[QLinkerInputBindingPatch] = Field(default_factory=list)
|
|
488
|
+
request: QLinkerRequestPatch
|
|
489
|
+
outputs: list[QLinkerOutputBindingPatch] = Field(default_factory=list)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
class CodeBlockOutputBindingPatch(StrictModel):
|
|
493
|
+
alias: str
|
|
494
|
+
path: str
|
|
495
|
+
target_field: FieldSelector = Field(
|
|
496
|
+
validation_alias=AliasChoices("target_field", "targetField"),
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
@model_validator(mode="before")
|
|
500
|
+
@classmethod
|
|
501
|
+
def normalize_aliases(cls, value: Any) -> Any:
|
|
502
|
+
if not isinstance(value, dict):
|
|
503
|
+
return value
|
|
504
|
+
payload = dict(value)
|
|
505
|
+
if "alias_name" in payload and "alias" not in payload:
|
|
506
|
+
payload["alias"] = payload.pop("alias_name")
|
|
507
|
+
if "aliasName" in payload and "alias" not in payload:
|
|
508
|
+
payload["alias"] = payload.pop("aliasName")
|
|
509
|
+
if "alias_path" in payload and "path" not in payload:
|
|
510
|
+
payload["path"] = payload.pop("alias_path")
|
|
511
|
+
if "aliasPath" in payload and "path" not in payload:
|
|
512
|
+
payload["path"] = payload.pop("aliasPath")
|
|
513
|
+
raw_target = payload.get("target_field", payload.get("targetField"))
|
|
514
|
+
if isinstance(raw_target, str):
|
|
515
|
+
payload["target_field"] = {"name": raw_target}
|
|
516
|
+
return payload
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
class CodeBlockBindingPatch(StrictModel):
|
|
520
|
+
inputs: list[CodeBlockInputBindingPatch] = Field(default_factory=list)
|
|
521
|
+
code: str = ""
|
|
522
|
+
auto_trigger: bool | None = Field(default=None, validation_alias=AliasChoices("auto_trigger", "autoTrigger"))
|
|
523
|
+
custom_button_text_enabled: bool | None = Field(
|
|
524
|
+
default=None,
|
|
525
|
+
validation_alias=AliasChoices("custom_button_text_enabled", "customButtonTextEnabled", "custom_btn_text_status", "customBtnTextStatus"),
|
|
526
|
+
)
|
|
527
|
+
custom_button_text: str | None = Field(
|
|
528
|
+
default=None,
|
|
529
|
+
validation_alias=AliasChoices("custom_button_text", "customButtonText", "custom_btn_text", "customBtnText"),
|
|
530
|
+
)
|
|
531
|
+
outputs: list[CodeBlockOutputBindingPatch] = Field(default_factory=list)
|
|
532
|
+
|
|
533
|
+
@model_validator(mode="before")
|
|
534
|
+
@classmethod
|
|
535
|
+
def normalize_aliases(cls, value: Any) -> Any:
|
|
536
|
+
if not isinstance(value, dict):
|
|
537
|
+
return value
|
|
538
|
+
payload = dict(value)
|
|
539
|
+
if "code_content" in payload and "code" not in payload:
|
|
540
|
+
payload["code"] = payload.pop("code_content")
|
|
541
|
+
if "codeContent" in payload and "code" not in payload:
|
|
542
|
+
payload["code"] = payload.pop("codeContent")
|
|
543
|
+
return payload
|
|
544
|
+
|
|
545
|
+
|
|
284
546
|
class FieldPatch(StrictModel):
|
|
285
547
|
name: str = Field(validation_alias=AliasChoices("name", "title", "label"))
|
|
286
548
|
type: PublicFieldType
|
|
@@ -291,6 +553,31 @@ class FieldPatch(StrictModel):
|
|
|
291
553
|
display_field: FieldSelector | None = None
|
|
292
554
|
visible_fields: list[FieldSelector] = Field(default_factory=list)
|
|
293
555
|
relation_mode: PublicRelationMode | None = Field(default=None, validation_alias=AliasChoices("relation_mode", "relationMode", "selection_mode", "selectionMode"))
|
|
556
|
+
remote_lookup_config: RemoteLookupConfigPatch | None = Field(
|
|
557
|
+
default=None,
|
|
558
|
+
validation_alias=AliasChoices("remote_lookup_config", "remoteLookupConfig"),
|
|
559
|
+
)
|
|
560
|
+
q_linker_binding: QLinkerBindingPatch | None = Field(
|
|
561
|
+
default=None,
|
|
562
|
+
validation_alias=AliasChoices("q_linker_binding", "qLinkerBinding"),
|
|
563
|
+
)
|
|
564
|
+
code_block_config: CodeBlockConfigPatch | None = Field(
|
|
565
|
+
default=None,
|
|
566
|
+
validation_alias=AliasChoices("code_block_config", "codeBlockConfig"),
|
|
567
|
+
)
|
|
568
|
+
code_block_binding: CodeBlockBindingPatch | None = Field(
|
|
569
|
+
default=None,
|
|
570
|
+
validation_alias=AliasChoices("code_block_binding", "codeBlockBinding"),
|
|
571
|
+
)
|
|
572
|
+
auto_trigger: bool | None = Field(default=None, validation_alias=AliasChoices("auto_trigger", "autoTrigger"))
|
|
573
|
+
custom_button_text_enabled: bool | None = Field(
|
|
574
|
+
default=None,
|
|
575
|
+
validation_alias=AliasChoices("custom_button_text_enabled", "customButtonTextEnabled", "custom_btn_text_status", "customBtnTextStatus"),
|
|
576
|
+
)
|
|
577
|
+
custom_button_text: str | None = Field(
|
|
578
|
+
default=None,
|
|
579
|
+
validation_alias=AliasChoices("custom_button_text", "customButtonText", "custom_btn_text", "customBtnText"),
|
|
580
|
+
)
|
|
294
581
|
subfields: list["FieldPatch"] = Field(default_factory=list)
|
|
295
582
|
|
|
296
583
|
@model_validator(mode="after")
|
|
@@ -301,6 +588,19 @@ class FieldPatch(StrictModel):
|
|
|
301
588
|
raise ValueError("target_app_key is only allowed for relation fields")
|
|
302
589
|
if self.type != PublicFieldType.relation and (self.display_field is not None or self.visible_fields or self.relation_mode is not None):
|
|
303
590
|
raise ValueError("display_field, visible_fields, and relation_mode are only allowed for relation fields")
|
|
591
|
+
if self.type != PublicFieldType.q_linker and (
|
|
592
|
+
self.remote_lookup_config is not None
|
|
593
|
+
or self.q_linker_binding is not None
|
|
594
|
+
):
|
|
595
|
+
raise ValueError("remote_lookup_config and q_linker_binding are only allowed for q_linker fields")
|
|
596
|
+
if self.type != PublicFieldType.code_block and (
|
|
597
|
+
self.code_block_config is not None
|
|
598
|
+
or self.code_block_binding is not None
|
|
599
|
+
or self.auto_trigger is not None
|
|
600
|
+
or self.custom_button_text_enabled is not None
|
|
601
|
+
or self.custom_button_text is not None
|
|
602
|
+
):
|
|
603
|
+
raise ValueError("code_block_config, code_block_binding, auto_trigger, custom_button_text_enabled, and custom_button_text are only allowed for code_block fields")
|
|
304
604
|
if self.type == PublicFieldType.subtable and not self.subfields:
|
|
305
605
|
raise ValueError("subtable field requires subfields")
|
|
306
606
|
if self.type != PublicFieldType.subtable and self.subfields:
|
|
@@ -323,6 +623,31 @@ class FieldMutation(StrictModel):
|
|
|
323
623
|
display_field: FieldSelector | None = None
|
|
324
624
|
visible_fields: list[FieldSelector] | None = None
|
|
325
625
|
relation_mode: PublicRelationMode | None = Field(default=None, validation_alias=AliasChoices("relation_mode", "relationMode", "selection_mode", "selectionMode"))
|
|
626
|
+
remote_lookup_config: RemoteLookupConfigPatch | None = Field(
|
|
627
|
+
default=None,
|
|
628
|
+
validation_alias=AliasChoices("remote_lookup_config", "remoteLookupConfig"),
|
|
629
|
+
)
|
|
630
|
+
q_linker_binding: QLinkerBindingPatch | None = Field(
|
|
631
|
+
default=None,
|
|
632
|
+
validation_alias=AliasChoices("q_linker_binding", "qLinkerBinding"),
|
|
633
|
+
)
|
|
634
|
+
code_block_config: CodeBlockConfigPatch | None = Field(
|
|
635
|
+
default=None,
|
|
636
|
+
validation_alias=AliasChoices("code_block_config", "codeBlockConfig"),
|
|
637
|
+
)
|
|
638
|
+
code_block_binding: CodeBlockBindingPatch | None = Field(
|
|
639
|
+
default=None,
|
|
640
|
+
validation_alias=AliasChoices("code_block_binding", "codeBlockBinding"),
|
|
641
|
+
)
|
|
642
|
+
auto_trigger: bool | None = Field(default=None, validation_alias=AliasChoices("auto_trigger", "autoTrigger"))
|
|
643
|
+
custom_button_text_enabled: bool | None = Field(
|
|
644
|
+
default=None,
|
|
645
|
+
validation_alias=AliasChoices("custom_button_text_enabled", "customButtonTextEnabled", "custom_btn_text_status", "customBtnTextStatus"),
|
|
646
|
+
)
|
|
647
|
+
custom_button_text: str | None = Field(
|
|
648
|
+
default=None,
|
|
649
|
+
validation_alias=AliasChoices("custom_button_text", "customButtonText", "custom_btn_text", "customBtnText"),
|
|
650
|
+
)
|
|
326
651
|
subfields: list[FieldPatch] | None = None
|
|
327
652
|
|
|
328
653
|
@model_validator(mode="after")
|
|
@@ -331,6 +656,19 @@ class FieldMutation(StrictModel):
|
|
|
331
656
|
raise ValueError("relation field requires target_app_key")
|
|
332
657
|
if self.type is not None and self.type != PublicFieldType.relation and (self.display_field is not None or self.visible_fields or self.relation_mode is not None):
|
|
333
658
|
raise ValueError("display_field, visible_fields, and relation_mode are only allowed for relation fields")
|
|
659
|
+
if self.type is not None and self.type != PublicFieldType.q_linker and (
|
|
660
|
+
self.remote_lookup_config is not None
|
|
661
|
+
or self.q_linker_binding is not None
|
|
662
|
+
):
|
|
663
|
+
raise ValueError("remote_lookup_config and q_linker_binding are only allowed for q_linker fields")
|
|
664
|
+
if self.type is not None and self.type != PublicFieldType.code_block and (
|
|
665
|
+
self.code_block_config is not None
|
|
666
|
+
or self.code_block_binding is not None
|
|
667
|
+
or self.auto_trigger is not None
|
|
668
|
+
or self.custom_button_text_enabled is not None
|
|
669
|
+
or self.custom_button_text is not None
|
|
670
|
+
):
|
|
671
|
+
raise ValueError("code_block_config, code_block_binding, auto_trigger, custom_button_text_enabled, and custom_button_text are only allowed for code_block fields")
|
|
334
672
|
if self.type == PublicFieldType.subtable and not self.subfields:
|
|
335
673
|
raise ValueError("subtable field requires subfields")
|
|
336
674
|
return self
|
|
@@ -385,8 +723,9 @@ def _normalize_layout_rows(value: Any, *, columns: int | None = None) -> Any:
|
|
|
385
723
|
|
|
386
724
|
|
|
387
725
|
class LayoutSectionPatch(StrictModel):
|
|
388
|
-
|
|
389
|
-
|
|
726
|
+
type: str | None = Field(default=None, validation_alias=AliasChoices("type", "kind", "block_type", "blockType"))
|
|
727
|
+
section_id: str | None = Field(default=None, validation_alias=AliasChoices("section_id", "sectionId", "paragraph_id", "paragraphId"))
|
|
728
|
+
title: str = Field(validation_alias=AliasChoices("title", "name", "paragraph_title", "paragraphTitle"))
|
|
390
729
|
rows: list[list[Any]] = Field(default_factory=list)
|
|
391
730
|
|
|
392
731
|
@model_validator(mode="before")
|
|
@@ -397,6 +736,10 @@ class LayoutSectionPatch(StrictModel):
|
|
|
397
736
|
payload = dict(value)
|
|
398
737
|
if "name" in payload and "title" not in payload:
|
|
399
738
|
payload["title"] = payload.pop("name")
|
|
739
|
+
if "paragraph_title" in payload and "title" not in payload:
|
|
740
|
+
payload["title"] = payload.pop("paragraph_title")
|
|
741
|
+
if "paragraphTitle" in payload and "title" not in payload:
|
|
742
|
+
payload["title"] = payload.pop("paragraphTitle")
|
|
400
743
|
shorthand: Any | None = None
|
|
401
744
|
if "rows" not in payload:
|
|
402
745
|
if "fields" in payload:
|
|
@@ -412,6 +755,8 @@ class LayoutSectionPatch(StrictModel):
|
|
|
412
755
|
|
|
413
756
|
@model_validator(mode="after")
|
|
414
757
|
def validate_rows(self) -> "LayoutSectionPatch":
|
|
758
|
+
if self.type is not None and str(self.type).strip().lower() != "paragraph":
|
|
759
|
+
raise ValueError("layout section type must be 'paragraph'")
|
|
415
760
|
if not self.rows:
|
|
416
761
|
raise ValueError("section rows must be a non-empty list")
|
|
417
762
|
for row in self.rows:
|
|
@@ -1211,5 +1556,6 @@ def _normalize_public_relation_mode(value: Any) -> str | None:
|
|
|
1211
1556
|
|
|
1212
1557
|
CustomButtonMatchRulePatch.model_rebuild()
|
|
1213
1558
|
CustomButtonAddDataConfigPatch.model_rebuild()
|
|
1559
|
+
CodeBlockAliasPathPatch.model_rebuild()
|
|
1214
1560
|
ViewButtonBindingPatch.model_rebuild()
|
|
1215
1561
|
ViewUpsertPatch.model_rebuild()
|