flowbase-cli 0.1.13__tar.gz → 0.1.14__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flowbase-cli
3
- Version: 0.1.13
3
+ Version: 0.1.14
4
4
  Summary: Command line client for the FlowBase OpenAPI
5
5
  Author: DeepFlowAI
6
6
  License: Apache-2.0
@@ -125,6 +125,7 @@ flowbase docs catalogs permissions # function permission points + data sc
125
125
  flowbase docs catalogs dashboard-filters # filter types + date-range presets
126
126
  flowbase docs catalogs page-components # Page component shapes, placement, fields, and enums
127
127
  flowbase docs catalogs page-rules # Page condition operators and action shapes
128
+ flowbase docs catalogs forms # custom form fields, rules, appearance, and settings
128
129
  flowbase docs catalogs id-prefixes # resource ID prefix meanings
129
130
  flowbase docs catalogs icons # icon guidance + examples
130
131
  ```
@@ -116,6 +116,7 @@ flowbase docs catalogs permissions # function permission points + data sc
116
116
  flowbase docs catalogs dashboard-filters # filter types + date-range presets
117
117
  flowbase docs catalogs page-components # Page component shapes, placement, fields, and enums
118
118
  flowbase docs catalogs page-rules # Page condition operators and action shapes
119
+ flowbase docs catalogs forms # custom form fields, rules, appearance, and settings
119
120
  flowbase docs catalogs id-prefixes # resource ID prefix meanings
120
121
  flowbase docs catalogs icons # icon guidance + examples
121
122
  ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "flowbase-cli"
7
- version = "0.1.13"
7
+ version = "0.1.14"
8
8
  description = "Command line client for the FlowBase OpenAPI"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,3 +1,3 @@
1
1
  """FlowBase OpenAPI CLI."""
2
2
 
3
- __version__ = "0.1.13"
3
+ __version__ = "0.1.14"
@@ -169,6 +169,26 @@ ENDPOINTS = (
169
169
  )),
170
170
  Endpoint("dashboards.copy", "flowbase_dashboards_copy", "POST", "/dashboards/{dashboard_id}/copy", "metadata:write", "Copy a dashboard (not added to any app).", "flowbase dashboards copy dsh_xxx"),
171
171
  Endpoint("dashboards.delete", "flowbase_dashboards_delete", "POST", "/dashboards/{dashboard_id}/delete", "metadata:write", "Logically delete a dashboard (also removed from all apps).", "flowbase dashboards delete dsh_xxx --yes", True),
172
+ Endpoint("forms.list", "flowbase_forms_list", "GET", "/forms", "metadata:read", "List custom forms with collection status.", "flowbase forms list"),
173
+ Endpoint("forms.get", "flowbase_forms_get", "GET", "/forms/{form_id}", "metadata:read", "Get a custom form definition and its placeable field options. Read docs catalogs forms before update.", "flowbase forms get frm_xxx"),
174
+ Endpoint("forms.create", "flowbase_forms_create", "POST", "/forms", "metadata:write", "Create a custom form draft; object-required fields are pre-added.", "flowbase forms create --label 'Website inquiry' --key website_inquiry --object-id obj_xxx",
175
+ request_body='{"label":"Website inquiry","key":"website_inquiry","objectId":"obj_xxx","description":""}'),
176
+ Endpoint("forms.update", "flowbase_forms_update", "PUT", "/forms/{form_id}", "metadata:write", "Update a custom form draft (full snapshot: fields, rules, appearance, settings). Read docs catalogs forms first.", "flowbase forms update frm_xxx --json-file form.json",
177
+ request_body=(
178
+ '{"label":"Website inquiry","description":"","baseRevision":3,'
179
+ '"fields":[{"fieldId":"fld_xxx","label":"","description":"","state":"required","prefill":false}],'
180
+ '"rules":[],'
181
+ '"appearance":{"title":"Website inquiry","description":"","logo":null,"cover":null,"themeColor":"#171717",'
182
+ '"background":"gray","submitText":"提交","language":"zh-CN"},'
183
+ '"settings":{"afterSubmit":"message","successTitle":"提交成功","successBody":"感谢你的填写",'
184
+ '"allowResubmit":true,"redirectUrl":"","deadline":null,"limit":null}}'
185
+ )),
186
+ Endpoint("forms.copy", "flowbase_forms_copy", "POST", "/forms/{form_id}/copy", "metadata:write", "Copy a custom form (collection state is not copied).", "flowbase forms copy frm_xxx"),
187
+ Endpoint("forms.delete", "flowbase_forms_delete", "POST", "/forms/{form_id}/delete", "metadata:write", "Logically delete a custom form.", "flowbase forms delete frm_xxx --yes", True),
188
+ Endpoint("forms.share.get", "flowbase_forms_share_get", "GET", "/forms/{form_id}/share", "metadata:read", "Get the public link and collection state in this environment.", "flowbase forms share frm_xxx"),
189
+ Endpoint("forms.share.set-accepting", "flowbase_forms_share_set_accepting", "PUT", "/forms/{form_id}/share/accepting", "metadata:write", "Turn public collection on or off in this environment (immediate, not a draft change).", "flowbase forms set-accepting frm_xxx --on",
190
+ request_body='{"accepting":true}'),
191
+ Endpoint("forms.share.reset", "flowbase_forms_share_reset", "POST", "/forms/{form_id}/share/reset", "metadata:write", "Reset the public link; the old link stops working immediately.", "flowbase forms reset-link frm_xxx --yes", True),
172
192
  Endpoint("objects.list", "flowbase_objects_list", "GET", "/objects", "metadata:read", "List object metadata.", "flowbase objects list"),
173
193
  Endpoint("objects.get", "flowbase_objects_get", "GET", "/objects/{object_id}", "metadata:read", "Get object and field metadata.", "flowbase objects get obj_xxx"),
174
194
  Endpoint("objects.create", "flowbase_objects_create", "POST", "/objects", "metadata:write", "Create an object draft.", "flowbase objects create --json-file object.json",
@@ -563,7 +583,8 @@ ENDPOINT_BY_KEY = {endpoint.key: endpoint for endpoint in ENDPOINTS}
563
583
 
564
584
  # Closed-domain catalogs for offline authoring of complex request bodies.
565
585
  # Keep in sync with server registries (field types, workflow/approval node kinds,
566
- # permission points, dashboard filter presets, page component kinds).
586
+ # permission points, dashboard filter presets, page component kinds, form
587
+ # snapshot validation).
567
588
  # Icons intentionally reference the server file because the full Tabler set is large.
568
589
  _PERMISSIONS = ("required", "optional", "readonly", "hidden")
569
590
  _TEXT_FILTERS = ("eq", "neq", "contains", "not_contains", "is_empty", "is_not_empty")
@@ -802,7 +823,10 @@ AUTHORING = {
802
823
  ],
803
824
  },
804
825
  "documentationKeys": "docs list returns endpoint keys. Use apps.objects.add for apps add-objects; docs show includes the CLI example.",
805
- "requiredUpdateFields": {"apps.update": ["label", "icon", "color"]},
826
+ "requiredUpdateFields": {
827
+ "apps.update": ["label", "icon", "color"],
828
+ "forms.update": ["label", "appearance", "settings"],
829
+ },
806
830
  "requiredCreateFields": {
807
831
  "objects.create": ["label", "key", "description"],
808
832
  "fields.create": ["label", "key", "description", "type"],
@@ -811,22 +835,27 @@ AUTHORING = {
811
835
  "pages.create": ["baseRevision", "page"],
812
836
  "records.create": ["values"],
813
837
  "print-templates.create": ["name", "html"],
838
+ "forms.create": ["label", "key", "objectId"],
814
839
  },
815
840
  "draftRevision": {
816
841
  "notReturnedBy": ["objects.get", "objects.list", "apps.get", "apps.list"],
817
842
  "returnedBy": [
818
843
  "views.list", "pages.list", "pages.get",
819
844
  "print-templates.list", "print-templates.get",
845
+ "forms.get",
820
846
  "every metadata write response (draftRevision)",
821
847
  ],
822
848
  "note": "Capture draftRevision from the last write or from views.list/pages.list/print-templates.list before writes.",
823
849
  },
824
850
  "readThenWrite": (
825
- "For update of views/pages/dashboards/fields with constraints: get the snapshot, "
826
- "edit it, set baseRevision from draftRevision. fields.update.constraints is a full replace; "
827
- "sending {} wipes options. print-templates.update is partial: send only changed fields, "
828
- "baseRevision, and baseUpdatedAt copied from updatedAt. Resending id, key, enabled, "
829
- "updatedAt, or draftRevision is rejected."
851
+ "For update of views/pages/dashboards/forms/fields with constraints: get the snapshot, "
852
+ "edit it, set baseRevision from draftRevision. forms.update unwraps forms.get: send "
853
+ "label, description, fields, rules, appearance, settings, and baseRevision; omit the "
854
+ "form wrapper, id, key, objectId, updatedAt, and fieldOptions. Omitting fields or rules "
855
+ "clears them. fields.update.constraints is a full replace; sending {} wipes options. "
856
+ "print-templates.update is partial: send only changed fields, baseRevision, and "
857
+ "baseUpdatedAt copied from updatedAt. Resending id, key, enabled, updatedAt, or "
858
+ "draftRevision is rejected."
830
859
  ),
831
860
  "recordsNeedReleasedMetadata": (
832
861
  "On production, records.create/list/get use the released runtime, not the current draft. "
@@ -840,6 +869,7 @@ AUTHORING = {
840
869
  "dashboards.run": "{ok,output,error,logs,durationMs,cached,mode:draft-preview}; inspect ok and error.",
841
870
  "apps.create": "{appId,draftRevision}",
842
871
  "dashboards.create": "{dashboardId,draftRevision}",
872
+ "forms.create": "{formId,draftRevision}",
843
873
  "workflows.create": "{workflowId,draftRevision}",
844
874
  "approvals.create": "{approvalId,draftRevision}",
845
875
  "print-templates.create": "{templateId,draftRevision}",
@@ -876,6 +906,10 @@ AUTHORING = {
876
906
  "wrong": "select/lookup filter or record value uses the display label",
877
907
  "right": "Use opt_/rec_/emp_ ids from objects.get / records.get.",
878
908
  },
909
+ {
910
+ "wrong": "forms update with the forms.get body, or fields omitted",
911
+ "right": "Unwrap form, keep every placed field, and set baseRevision from draftRevision. See docs catalogs forms.",
912
+ },
879
913
  {
880
914
  "wrong": "records.list.values is the full record",
881
915
  "right": "List is a column projection. records.get for all field keys.",
@@ -984,7 +1018,7 @@ APPROVAL_NODE_TYPES = (
984
1018
  )
985
1019
 
986
1020
  FUNCTION_PERMISSIONS = (
987
- "workspace.access", "app.manage", "object.manage", "dashboard.manage",
1021
+ "workspace.access", "app.manage", "object.manage", "dashboard.manage", "form.manage",
988
1022
  "version.manage", "org.manage", "system.manage",
989
1023
  )
990
1024
 
@@ -1285,7 +1319,8 @@ RESOURCE_ID_PREFIXES = {
1285
1319
  "reg_": "page region", "cmp_": "page component",
1286
1320
  "dsh_": "dashboard", "dcm_": "dashboard component", "rol_": "role",
1287
1321
  "rec_": "record", "att_": "attachment", "emp_": "employee", "dep_": "department",
1288
- "ptp_": "print template",
1322
+ "ptp_": "print template", "frm_": "custom form", "fim_": "form image",
1323
+ "rule_": "form or page rule",
1289
1324
  }
1290
1325
 
1291
1326
  WORKFLOW_BRANCHES = {
@@ -1379,6 +1414,110 @@ PRINT_TEMPLATES = {
1379
1414
  "verify": "The management CLI cannot render a template. get returns the source. A saved {{ }} that names the wrong key prints blank. There is no print-templates run command.",
1380
1415
  }
1381
1416
 
1417
+ FORMS = {
1418
+ "command": "flowbase docs catalogs forms",
1419
+ "readFirst": (
1420
+ "forms get returns form, fieldOptions, and draftRevision. "
1421
+ "fieldOptions[].availability is the placement contract: placeable, reason, states, "
1422
+ "preset, presetRequired, prefill, plus objectRequired and usableDefault. "
1423
+ "Use field IDs from fieldOptions, never field keys."
1424
+ ),
1425
+ "write": {
1426
+ "create": (
1427
+ "Requires label, key, and objectId. description defaults to empty. "
1428
+ "Placeable object-required fields are pre-added. key starts with a lowercase letter, "
1429
+ "contains only lowercase letters, digits, and underscores, max 50, and cannot change later."
1430
+ ),
1431
+ "update": (
1432
+ "Full snapshot. Requires label, appearance, and settings. "
1433
+ "fields and rules default to [] and clear the form when omitted. "
1434
+ "Copy draftRevision to baseRevision. Unwrap forms.get: do not send the form wrapper, "
1435
+ "id, key, objectId, updatedAt, or fieldOptions."
1436
+ ),
1437
+ "channel": (
1438
+ "set-accepting and reset-link take effect immediately in this environment and are not "
1439
+ "draft changes. copy does not copy collection state or the public link. delete and "
1440
+ "reset-link require --yes."
1441
+ ),
1442
+ },
1443
+ "limits": {
1444
+ "formsPerEnvironment": 200,
1445
+ "fields": 100,
1446
+ "rules": 50,
1447
+ "label": "1 to 50 characters",
1448
+ "description": "max 200 characters",
1449
+ "conditionsPerRule": "1 to 10",
1450
+ },
1451
+ "field": {
1452
+ "requiredKeys": ["fieldId", "label", "description", "state", "prefill"],
1453
+ "optionalKeys": ["preset"],
1454
+ "label": "Display override. Empty string keeps the object field label. Max 100.",
1455
+ "description": "Max 500. Empty string is allowed.",
1456
+ "state": (
1457
+ "One of availability.states. Ordinary fields use required, optional, readonly, hidden. "
1458
+ "lookup.record, lookup.record.multiple, lookup.employee, and lookup.department are "
1459
+ "hidden only. attachment cannot be readonly."
1460
+ ),
1461
+ "prefill": "true only when availability.prefill is true. Hidden-only, attachment, and text.rich cannot be prefilled.",
1462
+ "preset": (
1463
+ "Omit the key when there is no preset. Required when availability.presetRequired. "
1464
+ "A fixed value uses the record value shape from record-values. "
1465
+ "A dynamic preset is {\"mode\":\"dynamic\",\"value\":\"<dynamic default>\"}; currentEmployee is rejected."
1466
+ ),
1467
+ "objectRequired": (
1468
+ "Every placeable object-required field must stay in fields. It cannot be optional. "
1469
+ "readonly or hidden needs a preset unless usableDefault is true."
1470
+ ),
1471
+ "fillable": "At least one placed field is required or optional, or an enabled rule sets one of those states.",
1472
+ },
1473
+ "rule": {
1474
+ "requiredKeys": ["name", "enabled", "match", "conditions", "actions"],
1475
+ "optionalKeys": ["id"],
1476
+ "id": "Omit id, or send a value that does not start with rule_, to allocate a new rule_ id. Preserve an existing rule_ id on update.",
1477
+ "name": "1 to 50 characters, unique in the form.",
1478
+ "match": ["all", "any"],
1479
+ "conditions": (
1480
+ "Same operators as page-rules. Candidates are placed fields that are not "
1481
+ "lookup.record, lookup.record.multiple, lookup.employee, or lookup.department."
1482
+ ),
1483
+ "actions": (
1484
+ "Only setFieldState: {\"type\":\"setFieldState\",\"fieldId\":\"fld_...\",\"state\":\"required\"}. "
1485
+ "state must be in that field's availability.states. No component visibility."
1486
+ ),
1487
+ },
1488
+ "appearance": {
1489
+ "requiredKeys": [
1490
+ "title", "description", "logo", "cover", "themeColor",
1491
+ "background", "submitText", "language",
1492
+ ],
1493
+ "title": "1 to 100 characters.",
1494
+ "description": "Max 2000 characters.",
1495
+ "logo": "null or an existing fim_ id. The management CLI cannot upload images.",
1496
+ "cover": "null or an existing fim_ id. The management CLI cannot upload images.",
1497
+ "themeColor": "#RRGGBB",
1498
+ "background": ["gray", "white", "tint"],
1499
+ "submitText": "Max 20 characters. Empty string is allowed.",
1500
+ "language": ["zh-CN", "en"],
1501
+ },
1502
+ "settings": {
1503
+ "requiredKeys": [
1504
+ "afterSubmit", "successTitle", "successBody", "allowResubmit",
1505
+ "redirectUrl", "deadline", "limit",
1506
+ ],
1507
+ "afterSubmit": ["message", "redirect"],
1508
+ "successTitle": "Max 50 characters.",
1509
+ "successBody": "Max 500 characters.",
1510
+ "redirectUrl": "An https URL when afterSubmit is redirect, max 2048. Otherwise an empty string is allowed.",
1511
+ "deadline": "null, or an ISO-8601 datetime with a timezone. A newly set deadline cannot be in the past.",
1512
+ "limit": "null, or an integer from 1 to 1000000.",
1513
+ },
1514
+ "collectStatus": ["inactive", "invalid", "closed", "deadline", "full", "collecting"],
1515
+ "notInCli": (
1516
+ "Public form read, submit, and attachment upload need no API key and are not management commands. "
1517
+ "Logo and cover upload exist only on the signed-in admin API."
1518
+ ),
1519
+ }
1520
+
1382
1521
  CATALOGS = {
1383
1522
  "workflow-branches": WORKFLOW_BRANCHES,
1384
1523
  "approval-rules": APPROVAL_RULES,
@@ -1402,6 +1541,7 @@ CATALOGS = {
1402
1541
  },
1403
1542
  "page-components": PAGE_COMPONENTS,
1404
1543
  "page-rules": PAGE_RULES,
1544
+ "forms": FORMS,
1405
1545
  "id-prefixes": RESOURCE_ID_PREFIXES,
1406
1546
  "icons": {
1407
1547
  "note": "6146 Tabler icon names are supported; any Icon* name from the "
@@ -8,7 +8,8 @@ GUIDE = {
8
8
  "flowbase apps list;flowbase objects list:发现已有应用和对象,优先复用,不猜测 ID。",
9
9
  "flowbase objects get <object_id>:读取字段 key、类型、约束和真实选项 ID;关联字段继续读取目标对象。",
10
10
  "flowbase docs catalogs authoring;flowbase docs list;flowbase docs show <endpoint_key>:查请求和更新方式。",
11
- "配置页面前读取 pages options、page-components、page-rules、page-buttons;配置流程前读取 workflow-nodes、workflow-branches 或 approval-rules;配置打印模板前读取 print-templates。",
11
+ "配置页面前读取 pages options、page-components、page-rules、page-buttons;配置流程前读取 workflow-nodes、workflow-branches 或 approval-rules;配置打印模板前读取 print-templates;配置自定义表单前读取 forms。",
12
+ "表单先 forms get,按目录把快照拆成 label、fields、rules、appearance、settings 再 update,baseRevision 用返回的 draftRevision。对象必填且可放入的字段必须保留。set-accepting 与 reset-link 立即生效,不进草稿。",
12
13
  "先 get 再修改,保留未修改的配置。按目录转换更新 envelope;从最近读取或写入返回值获取 draftRevision,作为 baseRevision。",
13
14
  "写后回读验证。records create 返回 recordId,再 records get;values 使用字段 key,选择和关联使用真实 ID。",
14
15
  "遍历列表的所有分页。records list --view-id <view_id> 验证特定视图,records get 获取完整记录。",
@@ -31,6 +32,7 @@ GUIDE = {
31
32
  "docs/guide 离线可读;业务调用需要权限和已启用环境。",
32
33
  "页面规则求值不是浏览器渲染;仪表盘输出验证不是图表视觉验证。打印模板 get 返回源码,管理 CLI 不能生成打印内容;写错的 {{ }} 会保存成功并打印成空白。",
33
34
  "组织候选只读;不能通过管理 CLI 替员工做审批决定。",
35
+ "公开表单的读取、提交和附件上传不是管理命令。logo 与 cover 只能引用已有 fim_ 图片,管理 CLI 不能上传图片。",
34
36
  "删除对象/字段/页面是逻辑删除;没有物理 purge,审计与回收站会保留。",
35
37
  ],
36
38
  }
@@ -522,6 +522,40 @@ def handle_dashboards(args: argparse.Namespace) -> int:
522
522
  )
523
523
 
524
524
 
525
+ def handle_forms(args: argparse.Namespace) -> int:
526
+ action = args.forms_action
527
+ if action == "list":
528
+ return run_request(args, "forms.list")
529
+ if action == "create":
530
+ body = _payload_from_args(
531
+ args,
532
+ {
533
+ "label": args.label,
534
+ "key": args.key,
535
+ "objectId": args.object_id,
536
+ "description": args.description,
537
+ },
538
+ ("label", "key", "objectId"),
539
+ )
540
+ return run_request(args, "forms.create", body=body)
541
+ path_params = {"form_id": args.form_id}
542
+ if action == "get":
543
+ return run_request(args, "forms.get", path_params=path_params)
544
+ if action == "update":
545
+ body = load_json_payload(args.json, args.json_file)
546
+ require_fields(body, "label", "appearance", "settings")
547
+ return run_request(args, "forms.update", path_params=path_params, body=body)
548
+ if action == "copy":
549
+ return run_request(args, "forms.copy", path_params=path_params)
550
+ if action == "share":
551
+ return run_request(args, "forms.share.get", path_params=path_params)
552
+ if action == "set-accepting":
553
+ return run_request(args, "forms.share.set-accepting", path_params=path_params, body={"accepting": args.on})
554
+ if action == "reset-link":
555
+ return run_request(args, "forms.share.reset", path_params=path_params)
556
+ return run_request(args, "forms.delete", path_params=path_params)
557
+
558
+
525
559
  def handle_groups(args: argparse.Namespace) -> int:
526
560
  action = args.groups_action
527
561
  path = {"app_id": args.app_id}
@@ -1136,6 +1170,37 @@ def build_parser() -> argparse.ArgumentParser:
1136
1170
  add_yes(dashboard_delete)
1137
1171
  dashboards.set_defaults(handler=handle_dashboards)
1138
1172
 
1173
+ forms = groups.add_parser("forms", help="Manage custom forms (public collection links).")
1174
+ form_actions = forms.add_subparsers(dest="forms_action", required=True)
1175
+ form_actions.add_parser("list")
1176
+ form_get = form_actions.add_parser("get")
1177
+ form_get.add_argument("form_id")
1178
+ form_create = form_actions.add_parser("create")
1179
+ form_create.add_argument("--label")
1180
+ form_create.add_argument("--key")
1181
+ form_create.add_argument("--object-id")
1182
+ form_create.add_argument("--description")
1183
+ add_json_args(form_create, endpoint_key="forms.create")
1184
+ form_update = form_actions.add_parser("update")
1185
+ form_update.add_argument("form_id")
1186
+ add_json_args(form_update, endpoint_key="forms.update")
1187
+ form_copy = form_actions.add_parser("copy")
1188
+ form_copy.add_argument("form_id")
1189
+ form_delete = form_actions.add_parser("delete")
1190
+ form_delete.add_argument("form_id")
1191
+ add_yes(form_delete)
1192
+ form_share = form_actions.add_parser("share", help="Show the public link and collection state.")
1193
+ form_share.add_argument("form_id")
1194
+ form_accepting = form_actions.add_parser("set-accepting", help="Turn public collection on or off (immediate).")
1195
+ form_accepting.add_argument("form_id")
1196
+ accepting_group = form_accepting.add_mutually_exclusive_group(required=True)
1197
+ accepting_group.add_argument("--on", dest="on", action="store_true")
1198
+ accepting_group.add_argument("--off", dest="on", action="store_false")
1199
+ form_reset = form_actions.add_parser("reset-link", help="Reset the public link; the old link stops working.")
1200
+ form_reset.add_argument("form_id")
1201
+ add_yes(form_reset)
1202
+ forms.set_defaults(handler=handle_forms)
1203
+
1139
1204
  app_groups = groups.add_parser("groups", help="Manage app object groups.")
1140
1205
  group_actions = app_groups.add_subparsers(dest="groups_action", required=True)
1141
1206
  group_create = group_actions.add_parser("create")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flowbase-cli
3
- Version: 0.1.13
3
+ Version: 0.1.14
4
4
  Summary: Command line client for the FlowBase OpenAPI
5
5
  Author: DeepFlowAI
6
6
  License: Apache-2.0
@@ -125,6 +125,7 @@ flowbase docs catalogs permissions # function permission points + data sc
125
125
  flowbase docs catalogs dashboard-filters # filter types + date-range presets
126
126
  flowbase docs catalogs page-components # Page component shapes, placement, fields, and enums
127
127
  flowbase docs catalogs page-rules # Page condition operators and action shapes
128
+ flowbase docs catalogs forms # custom form fields, rules, appearance, and settings
128
129
  flowbase docs catalogs id-prefixes # resource ID prefix meanings
129
130
  flowbase docs catalogs icons # icon guidance + examples
130
131
  ```
@@ -335,6 +335,22 @@ class FlowBaseCliTest(unittest.TestCase):
335
335
  self.assertEqual(prefixes["reg_"], "page region")
336
336
  self.assertEqual(prefixes["cmp_"], "page component")
337
337
  self.assertEqual(prefixes["ptp_"], "print template")
338
+ self.assertEqual(prefixes["frm_"], "custom form")
339
+ self.assertEqual(prefixes["fim_"], "form image")
340
+
341
+ stdout = io.StringIO()
342
+ with patch("sys.stdout", stdout):
343
+ code = main(["docs", "catalogs", "forms"])
344
+ self.assertEqual(code, 0)
345
+ forms = json.loads(stdout.getvalue())
346
+ self.assertEqual(
347
+ forms["field"]["requiredKeys"],
348
+ ["fieldId", "label", "description", "state", "prefill"],
349
+ )
350
+ self.assertEqual(forms["appearance"]["background"], ["gray", "white", "tint"])
351
+ self.assertEqual(forms["settings"]["afterSubmit"], ["message", "redirect"])
352
+ self.assertIn("setFieldState", forms["rule"]["actions"])
353
+ self.assertIn("forms", names)
338
354
 
339
355
  def test_docs_list_marks_body_and_confirmation(self):
340
356
  stdout = io.StringIO()
File without changes