flowbase-cli 0.1.15__tar.gz → 0.1.17__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.
- {flowbase_cli-0.1.15 → flowbase_cli-0.1.17}/PKG-INFO +103 -3
- {flowbase_cli-0.1.15 → flowbase_cli-0.1.17}/README.md +102 -2
- {flowbase_cli-0.1.15 → flowbase_cli-0.1.17}/pyproject.toml +1 -1
- {flowbase_cli-0.1.15 → flowbase_cli-0.1.17}/src/flowbase_cli/__init__.py +1 -1
- {flowbase_cli-0.1.15 → flowbase_cli-0.1.17}/src/flowbase_cli/endpoints.py +94 -7
- {flowbase_cli-0.1.15 → flowbase_cli-0.1.17}/src/flowbase_cli/guide.py +2 -0
- {flowbase_cli-0.1.15 → flowbase_cli-0.1.17}/src/flowbase_cli/main.py +99 -3
- {flowbase_cli-0.1.15 → flowbase_cli-0.1.17}/src/flowbase_cli.egg-info/PKG-INFO +103 -3
- {flowbase_cli-0.1.15 → flowbase_cli-0.1.17}/src/flowbase_cli.egg-info/SOURCES.txt +1 -0
- {flowbase_cli-0.1.15 → flowbase_cli-0.1.17}/tests/test_cli.py +1 -1
- flowbase_cli-0.1.17/tests/test_iframes.py +30 -0
- {flowbase_cli-0.1.15 → flowbase_cli-0.1.17}/setup.cfg +0 -0
- {flowbase_cli-0.1.15 → flowbase_cli-0.1.17}/src/flowbase_cli.egg-info/dependency_links.txt +0 -0
- {flowbase_cli-0.1.15 → flowbase_cli-0.1.17}/src/flowbase_cli.egg-info/entry_points.txt +0 -0
- {flowbase_cli-0.1.15 → flowbase_cli-0.1.17}/src/flowbase_cli.egg-info/top_level.txt +0 -0
- {flowbase_cli-0.1.15 → flowbase_cli-0.1.17}/tests/test_autonomy.py +0 -0
- {flowbase_cli-0.1.15 → flowbase_cli-0.1.17}/tests/test_recent_commands.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flowbase-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.17
|
|
4
4
|
Summary: Command line client for the FlowBase OpenAPI
|
|
5
5
|
Author: DeepFlowAI
|
|
6
6
|
License: Apache-2.0
|
|
@@ -76,7 +76,7 @@ flowbase
|
|
|
76
76
|
├── docs list | show | example | operations Offline endpoint catalog
|
|
77
77
|
├── guide Offline product and task guide (--format markdown|json)
|
|
78
78
|
├── organization employees | departments
|
|
79
|
-
├── apps list | get | create | update | set-enabled | delete
|
|
79
|
+
├── apps list | get | create | update | set-enabled | delete | navigation
|
|
80
80
|
│ add-objects | remove-object | set-group | reorder-objects
|
|
81
81
|
│ add-dashboards | remove-dashboard | set-dashboard-group | reorder-members
|
|
82
82
|
├── groups create | reorder | update | delete App groups (under an app)
|
|
@@ -88,6 +88,7 @@ flowbase
|
|
|
88
88
|
├── approvals list | get | create | update | reorder | set-enabled | delete
|
|
89
89
|
├── pages list | options | get | create | update | set-default | clear-default | delete | restore | evaluate
|
|
90
90
|
├── dashboards list | get | create | update | copy | delete | run
|
|
91
|
+
│ record-view-options | record-view-preview | records
|
|
91
92
|
├── changes commit | release | direct-release
|
|
92
93
|
├── versions list | show | releases Read-only history
|
|
93
94
|
├── environments restart Destructive test-env reset
|
|
@@ -138,7 +139,8 @@ Most `update` commands replace a **full snapshot** and accept an optional `baseR
|
|
|
138
139
|
|
|
139
140
|
```bash
|
|
140
141
|
flowbase dashboards get dsh_xxx > current.json # authoritative snapshot
|
|
141
|
-
#
|
|
142
|
+
# Keep only label/description/icon/filters/components; use draftRevision as baseRevision.
|
|
143
|
+
# Edit the desired fields while preserving all other components and filters.
|
|
142
144
|
flowbase dashboards update dsh_xxx --json-file current.json
|
|
143
145
|
```
|
|
144
146
|
|
|
@@ -160,6 +162,14 @@ flowbase objects cli-edit-policy update obj_xxx --json '{"baseRevision":4,"polic
|
|
|
160
162
|
|
|
161
163
|
`records create` returns `{recordId}`; use it with `records get`.
|
|
162
164
|
|
|
165
|
+
To add existing objects to an application, pass their IDs with `--object-ids`:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
flowbase apps add-objects app_xxx --object-ids obj_a,obj_b
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Use a comma-separated list for multiple objects. Object IDs are not positional arguments. Run `flowbase docs show apps.objects.add` for the endpoint details.
|
|
172
|
+
|
|
163
173
|
Record `values` keys are **field keys** (`name`), not internal IDs (`fld_...`). IDs are `obj_`/`fld_`/`dsh_`/`wfl_`/`pag_`/`reg_`/`cmp_`-prefixed; see `docs catalogs id-prefixes`.
|
|
164
174
|
|
|
165
175
|
### 6.2 Edit and publish configuration on a test environment
|
|
@@ -178,6 +188,31 @@ flowbase changes release --message "Ship sales dashboard" --yes
|
|
|
178
188
|
|
|
179
189
|
Dashboard component code runs server-side in a sandbox and returns ECharts config / metric / table / markdown. Components and filters are validated on save; use `flowbase dashboards get <id>` to discover component IDs, then `flowbase dashboards run <dashboard_id> <component_id>` to inspect `ok/output/error/logs` in a read-only draft preview (requires metadata:read and records:read). Visual chart rendering still requires the web UI.
|
|
180
190
|
|
|
191
|
+
App sections and record-view components:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
flowbase apps get app_xxx
|
|
195
|
+
flowbase docs catalogs app-sections
|
|
196
|
+
flowbase apps navigation app_xxx --json '{"type":"appSection.create","name":"Sales"}' --yes
|
|
197
|
+
flowbase groups create app_xxx --name Orders --section-id sec_xxx
|
|
198
|
+
flowbase apps add-objects app_xxx --object-ids obj_xxx --section-id sec_xxx
|
|
199
|
+
# add-dashboards and add-iframes also accept --section-id.
|
|
200
|
+
flowbase apps navigation app_xxx --json '{"type":"appMember.move","id":"obj_xxx","sectionId":"sec_xxx","groupId":"grp_xxx","expectedSectionId":"sec_xxx","expectedGroupId":null}' --yes
|
|
201
|
+
|
|
202
|
+
flowbase docs catalogs dashboard-record-views
|
|
203
|
+
flowbase dashboards record-view-options
|
|
204
|
+
flowbase docs example dashboards.record-view-preview --output preview.json
|
|
205
|
+
# Replace placeholder IDs and configure component/filters before previewing.
|
|
206
|
+
flowbase dashboards record-view-preview dsh_xxx --json-file preview.json
|
|
207
|
+
flowbase dashboards records dsh_xxx dcm_xxx --page 1 --query Orders --json '{"filterValues":{}}'
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`apps get` preserves section definitions and group/member `sectionId`. Navigation supports section creation, rename, empty-section deletion, full section ordering, group moves and member moves. Moves require explicit source and target positions; use JSON `null` for unassigned/ungrouped. See `app-sections` for all six request bodies. Navigation requires confirmation (`--yes` for automation), and writes only a draft.
|
|
211
|
+
|
|
212
|
+
Record-view previews and queries require both `metadata:read` and `records:read`, use the current Host's records and draft configuration, and return `mode: draft-preview` plus `draftRevision`. They use API-key visibility rather than an employee's workspace identity; hidden fields stay excluded and hidden filter fields are rejected. Page size is configured on the component (10/20/50). Search is ignored when `showSearch` is false. `dashboards run` is for code components.
|
|
213
|
+
|
|
214
|
+
For `dashboards update`, construct the body from the latest `get`: preserve `label`, `description`, `icon`, `filters`, and **all** `components`; set `baseRevision` to the returned `draftRevision`. Omit response-only fields. Keep existing component IDs and `recordView` unchanged when editing other properties; omit IDs on new components. Omitting `components` or `filters` clears them.
|
|
215
|
+
|
|
181
216
|
### 6.3 Production hotfix
|
|
182
217
|
|
|
183
218
|
```bash
|
|
@@ -289,3 +324,68 @@ Transport requests time out after 60 seconds. A lost write response has an unkno
|
|
|
289
324
|
- `flowbase docs catalogs workflow-python`: record creation examples and failed-task retry. `workflows python-runtime OBJECT new` discovers current object bindings; `workflows executions` includes Python attempts, logs and `canRetry`.
|
|
290
325
|
|
|
291
326
|
Use `records list OBJECT --view-id VIEW --view-layout` for tree/Gantt projections; the default remains a flat list. `records update` accepts `updatedAt` and `planViewId` in its JSON body for version-checked plan edits. Formula validation/trials require both metadata read and record read; workflow retry requires both metadata write and record write and explicit CLI confirmation (`--yes` in automation).
|
|
327
|
+
|
|
328
|
+
### Reusable iframe configuration (4.11)
|
|
329
|
+
|
|
330
|
+
`flowbase iframes list|get|create|update|delete` manages environment-specific iframe drafts. Create/update use `--json-file`; fields are `label`, `icon` (default `IconWorld`), `color` (default `#2563EB`), and a complete HTTPS `url` without credentials. Update and delete require `baseRevision` from `iframes get`; deletion also requires confirmation (`--yes` for noninteractive use).
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
flowbase iframes create --json-file iframe.json
|
|
334
|
+
flowbase iframes get ifr_example
|
|
335
|
+
flowbase iframes update ifr_example --json-file iframe-update.json
|
|
336
|
+
flowbase apps add-iframes app_example ifr_example
|
|
337
|
+
flowbase apps set-iframe-group app_example ifr_example --group-id grp_example
|
|
338
|
+
flowbase apps set-iframe-group app_example ifr_example --ungrouped
|
|
339
|
+
flowbase apps reorder-members app_example --json '{"items":[{"type":"iframe","id":"ifr_example"}]}'
|
|
340
|
+
flowbase apps remove-iframe app_example ifr_example --yes
|
|
341
|
+
flowbase iframes delete ifr_example --json '{"baseRevision":12}' --yes
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Removal only unlinks one app. Deletion unlinks all apps atomically. Changes follow the existing commit/release/rollback workflow; they do not fetch, proxy or modify the external website. API keys use `metadata:read`/`metadata:write`; browser roles use `iframe.manage` for shared resources and `app.manage` for app associations.
|
|
345
|
+
|
|
346
|
+
### Custom object views (4.12)
|
|
347
|
+
|
|
348
|
+
Read `flowbase docs catalogs custom-views` and `flowbase views list OBJECT` first. Save the following as `view.json`, replacing `baseRevision` with the current `draftRevision`:
|
|
349
|
+
|
|
350
|
+
```json
|
|
351
|
+
{
|
|
352
|
+
"baseRevision": 4,
|
|
353
|
+
"name": "Custom overview",
|
|
354
|
+
"enabled": false,
|
|
355
|
+
"defaultFieldIds": [],
|
|
356
|
+
"displayMode": "custom",
|
|
357
|
+
"custom": {
|
|
358
|
+
"runtimeVersion": "cv1",
|
|
359
|
+
"source": "import React from 'react'; export default function View(){return <div>Hello</div>}"
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
flowbase views create obj_example --json-file view.json
|
|
366
|
+
flowbase views list obj_example
|
|
367
|
+
flowbase views update obj_example viw_example --json-file view.json
|
|
368
|
+
flowbase views set-enabled obj_example viw_example --base-revision 6 --enabled --yes
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Use the revision returned by the preceding write, rather than the example numbers. Custom views start disabled; preview in the browser and enable when ready. Updates replace the writable snapshot: preserve `custom.source`, `custom.runtimeVersion`, `displayMode`, filters and other configuration from `views list`, excluding the response-only `id` and `status`. Built-in/custom view types cannot be converted. JSX source is limited to 512 KiB UTF-8; the runtime supports `react`, `three` and `flowbase`. CLI configuration does not execute browser previews.
|
|
372
|
+
|
|
373
|
+
### Record detail embedded pages (4.13)
|
|
374
|
+
|
|
375
|
+
`flowbase docs catalogs page-components` documents `recordEmbed`, supported at the top level or inside tabs of a detail Page. Add this component to an existing region column (or a tab's `components`):
|
|
376
|
+
|
|
377
|
+
```json
|
|
378
|
+
{
|
|
379
|
+
"kind": "recordEmbed",
|
|
380
|
+
"label": "External panel",
|
|
381
|
+
"showTitle": true,
|
|
382
|
+
"url": "https://example.com/panel",
|
|
383
|
+
"height": 480,
|
|
384
|
+
"allowRead": false,
|
|
385
|
+
"allowedFieldIds": []
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
Read `flowbase pages get PAGE`, preserve the existing components and IDs, and remove the read-only Page keys listed in `docs catalogs page-components`. Wrap the editable snapshot as `{"baseRevision":4,"page":{...}}`, then use `flowbase pages update PAGE --json-file page.json` (or `pages create --json-file page.json` for a new detail Page). For creation, omit the Page ID and IDs of new regions/components so the server allocates them.
|
|
390
|
+
|
|
391
|
+
Height must be an integer from 240 to 1600. URLs must use external HTTPS and exclude credentials, identity/context parameters and templates. To permit SDK reads, set `allowRead: true` and select at least one unique active field ID in `allowedFieldIds`; current record/field permissions still apply. The browser SDK is `/sdk/record-embed.v1.js`. The CLI configures the component; rendering and SDK calls run in the browser. `flowbase-user` has no metadata management commands.
|
|
@@ -67,7 +67,7 @@ flowbase
|
|
|
67
67
|
├── docs list | show | example | operations Offline endpoint catalog
|
|
68
68
|
├── guide Offline product and task guide (--format markdown|json)
|
|
69
69
|
├── organization employees | departments
|
|
70
|
-
├── apps list | get | create | update | set-enabled | delete
|
|
70
|
+
├── apps list | get | create | update | set-enabled | delete | navigation
|
|
71
71
|
│ add-objects | remove-object | set-group | reorder-objects
|
|
72
72
|
│ add-dashboards | remove-dashboard | set-dashboard-group | reorder-members
|
|
73
73
|
├── groups create | reorder | update | delete App groups (under an app)
|
|
@@ -79,6 +79,7 @@ flowbase
|
|
|
79
79
|
├── approvals list | get | create | update | reorder | set-enabled | delete
|
|
80
80
|
├── pages list | options | get | create | update | set-default | clear-default | delete | restore | evaluate
|
|
81
81
|
├── dashboards list | get | create | update | copy | delete | run
|
|
82
|
+
│ record-view-options | record-view-preview | records
|
|
82
83
|
├── changes commit | release | direct-release
|
|
83
84
|
├── versions list | show | releases Read-only history
|
|
84
85
|
├── environments restart Destructive test-env reset
|
|
@@ -129,7 +130,8 @@ Most `update` commands replace a **full snapshot** and accept an optional `baseR
|
|
|
129
130
|
|
|
130
131
|
```bash
|
|
131
132
|
flowbase dashboards get dsh_xxx > current.json # authoritative snapshot
|
|
132
|
-
#
|
|
133
|
+
# Keep only label/description/icon/filters/components; use draftRevision as baseRevision.
|
|
134
|
+
# Edit the desired fields while preserving all other components and filters.
|
|
133
135
|
flowbase dashboards update dsh_xxx --json-file current.json
|
|
134
136
|
```
|
|
135
137
|
|
|
@@ -151,6 +153,14 @@ flowbase objects cli-edit-policy update obj_xxx --json '{"baseRevision":4,"polic
|
|
|
151
153
|
|
|
152
154
|
`records create` returns `{recordId}`; use it with `records get`.
|
|
153
155
|
|
|
156
|
+
To add existing objects to an application, pass their IDs with `--object-ids`:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
flowbase apps add-objects app_xxx --object-ids obj_a,obj_b
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Use a comma-separated list for multiple objects. Object IDs are not positional arguments. Run `flowbase docs show apps.objects.add` for the endpoint details.
|
|
163
|
+
|
|
154
164
|
Record `values` keys are **field keys** (`name`), not internal IDs (`fld_...`). IDs are `obj_`/`fld_`/`dsh_`/`wfl_`/`pag_`/`reg_`/`cmp_`-prefixed; see `docs catalogs id-prefixes`.
|
|
155
165
|
|
|
156
166
|
### 6.2 Edit and publish configuration on a test environment
|
|
@@ -169,6 +179,31 @@ flowbase changes release --message "Ship sales dashboard" --yes
|
|
|
169
179
|
|
|
170
180
|
Dashboard component code runs server-side in a sandbox and returns ECharts config / metric / table / markdown. Components and filters are validated on save; use `flowbase dashboards get <id>` to discover component IDs, then `flowbase dashboards run <dashboard_id> <component_id>` to inspect `ok/output/error/logs` in a read-only draft preview (requires metadata:read and records:read). Visual chart rendering still requires the web UI.
|
|
171
181
|
|
|
182
|
+
App sections and record-view components:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
flowbase apps get app_xxx
|
|
186
|
+
flowbase docs catalogs app-sections
|
|
187
|
+
flowbase apps navigation app_xxx --json '{"type":"appSection.create","name":"Sales"}' --yes
|
|
188
|
+
flowbase groups create app_xxx --name Orders --section-id sec_xxx
|
|
189
|
+
flowbase apps add-objects app_xxx --object-ids obj_xxx --section-id sec_xxx
|
|
190
|
+
# add-dashboards and add-iframes also accept --section-id.
|
|
191
|
+
flowbase apps navigation app_xxx --json '{"type":"appMember.move","id":"obj_xxx","sectionId":"sec_xxx","groupId":"grp_xxx","expectedSectionId":"sec_xxx","expectedGroupId":null}' --yes
|
|
192
|
+
|
|
193
|
+
flowbase docs catalogs dashboard-record-views
|
|
194
|
+
flowbase dashboards record-view-options
|
|
195
|
+
flowbase docs example dashboards.record-view-preview --output preview.json
|
|
196
|
+
# Replace placeholder IDs and configure component/filters before previewing.
|
|
197
|
+
flowbase dashboards record-view-preview dsh_xxx --json-file preview.json
|
|
198
|
+
flowbase dashboards records dsh_xxx dcm_xxx --page 1 --query Orders --json '{"filterValues":{}}'
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
`apps get` preserves section definitions and group/member `sectionId`. Navigation supports section creation, rename, empty-section deletion, full section ordering, group moves and member moves. Moves require explicit source and target positions; use JSON `null` for unassigned/ungrouped. See `app-sections` for all six request bodies. Navigation requires confirmation (`--yes` for automation), and writes only a draft.
|
|
202
|
+
|
|
203
|
+
Record-view previews and queries require both `metadata:read` and `records:read`, use the current Host's records and draft configuration, and return `mode: draft-preview` plus `draftRevision`. They use API-key visibility rather than an employee's workspace identity; hidden fields stay excluded and hidden filter fields are rejected. Page size is configured on the component (10/20/50). Search is ignored when `showSearch` is false. `dashboards run` is for code components.
|
|
204
|
+
|
|
205
|
+
For `dashboards update`, construct the body from the latest `get`: preserve `label`, `description`, `icon`, `filters`, and **all** `components`; set `baseRevision` to the returned `draftRevision`. Omit response-only fields. Keep existing component IDs and `recordView` unchanged when editing other properties; omit IDs on new components. Omitting `components` or `filters` clears them.
|
|
206
|
+
|
|
172
207
|
### 6.3 Production hotfix
|
|
173
208
|
|
|
174
209
|
```bash
|
|
@@ -280,3 +315,68 @@ Transport requests time out after 60 seconds. A lost write response has an unkno
|
|
|
280
315
|
- `flowbase docs catalogs workflow-python`: record creation examples and failed-task retry. `workflows python-runtime OBJECT new` discovers current object bindings; `workflows executions` includes Python attempts, logs and `canRetry`.
|
|
281
316
|
|
|
282
317
|
Use `records list OBJECT --view-id VIEW --view-layout` for tree/Gantt projections; the default remains a flat list. `records update` accepts `updatedAt` and `planViewId` in its JSON body for version-checked plan edits. Formula validation/trials require both metadata read and record read; workflow retry requires both metadata write and record write and explicit CLI confirmation (`--yes` in automation).
|
|
318
|
+
|
|
319
|
+
### Reusable iframe configuration (4.11)
|
|
320
|
+
|
|
321
|
+
`flowbase iframes list|get|create|update|delete` manages environment-specific iframe drafts. Create/update use `--json-file`; fields are `label`, `icon` (default `IconWorld`), `color` (default `#2563EB`), and a complete HTTPS `url` without credentials. Update and delete require `baseRevision` from `iframes get`; deletion also requires confirmation (`--yes` for noninteractive use).
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
flowbase iframes create --json-file iframe.json
|
|
325
|
+
flowbase iframes get ifr_example
|
|
326
|
+
flowbase iframes update ifr_example --json-file iframe-update.json
|
|
327
|
+
flowbase apps add-iframes app_example ifr_example
|
|
328
|
+
flowbase apps set-iframe-group app_example ifr_example --group-id grp_example
|
|
329
|
+
flowbase apps set-iframe-group app_example ifr_example --ungrouped
|
|
330
|
+
flowbase apps reorder-members app_example --json '{"items":[{"type":"iframe","id":"ifr_example"}]}'
|
|
331
|
+
flowbase apps remove-iframe app_example ifr_example --yes
|
|
332
|
+
flowbase iframes delete ifr_example --json '{"baseRevision":12}' --yes
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Removal only unlinks one app. Deletion unlinks all apps atomically. Changes follow the existing commit/release/rollback workflow; they do not fetch, proxy or modify the external website. API keys use `metadata:read`/`metadata:write`; browser roles use `iframe.manage` for shared resources and `app.manage` for app associations.
|
|
336
|
+
|
|
337
|
+
### Custom object views (4.12)
|
|
338
|
+
|
|
339
|
+
Read `flowbase docs catalogs custom-views` and `flowbase views list OBJECT` first. Save the following as `view.json`, replacing `baseRevision` with the current `draftRevision`:
|
|
340
|
+
|
|
341
|
+
```json
|
|
342
|
+
{
|
|
343
|
+
"baseRevision": 4,
|
|
344
|
+
"name": "Custom overview",
|
|
345
|
+
"enabled": false,
|
|
346
|
+
"defaultFieldIds": [],
|
|
347
|
+
"displayMode": "custom",
|
|
348
|
+
"custom": {
|
|
349
|
+
"runtimeVersion": "cv1",
|
|
350
|
+
"source": "import React from 'react'; export default function View(){return <div>Hello</div>}"
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
flowbase views create obj_example --json-file view.json
|
|
357
|
+
flowbase views list obj_example
|
|
358
|
+
flowbase views update obj_example viw_example --json-file view.json
|
|
359
|
+
flowbase views set-enabled obj_example viw_example --base-revision 6 --enabled --yes
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Use the revision returned by the preceding write, rather than the example numbers. Custom views start disabled; preview in the browser and enable when ready. Updates replace the writable snapshot: preserve `custom.source`, `custom.runtimeVersion`, `displayMode`, filters and other configuration from `views list`, excluding the response-only `id` and `status`. Built-in/custom view types cannot be converted. JSX source is limited to 512 KiB UTF-8; the runtime supports `react`, `three` and `flowbase`. CLI configuration does not execute browser previews.
|
|
363
|
+
|
|
364
|
+
### Record detail embedded pages (4.13)
|
|
365
|
+
|
|
366
|
+
`flowbase docs catalogs page-components` documents `recordEmbed`, supported at the top level or inside tabs of a detail Page. Add this component to an existing region column (or a tab's `components`):
|
|
367
|
+
|
|
368
|
+
```json
|
|
369
|
+
{
|
|
370
|
+
"kind": "recordEmbed",
|
|
371
|
+
"label": "External panel",
|
|
372
|
+
"showTitle": true,
|
|
373
|
+
"url": "https://example.com/panel",
|
|
374
|
+
"height": 480,
|
|
375
|
+
"allowRead": false,
|
|
376
|
+
"allowedFieldIds": []
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Read `flowbase pages get PAGE`, preserve the existing components and IDs, and remove the read-only Page keys listed in `docs catalogs page-components`. Wrap the editable snapshot as `{"baseRevision":4,"page":{...}}`, then use `flowbase pages update PAGE --json-file page.json` (or `pages create --json-file page.json` for a new detail Page). For creation, omit the Page ID and IDs of new regions/components so the server allocates them.
|
|
381
|
+
|
|
382
|
+
Height must be an integer from 240 to 1600. URLs must use external HTTPS and exclude credentials, identity/context parameters and templates. To permit SDK reads, set `allowRead: true` and select at least one unique active field ID in `allowedFieldIds`; current record/field permissions still apply. The browser SDK is `/sdk/record-embed.v1.js`. The CLI configures the component; rendering and SDK calls run in the browser. `flowbase-user` has no metadata management commands.
|
|
@@ -22,6 +22,46 @@ class Endpoint:
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
ENDPOINTS = (
|
|
25
|
+
Endpoint("apps.navigation", "flowbase_apps_navigation", "POST", "/apps/{app_id}/navigation", "metadata:write", "Edit app sections and navigation positions; read apps get first. See app-sections catalog.", "flowbase apps navigation app_xxx --json-file navigation.json --yes", True, request_body='{"type":"appSection.create","name":"Sales"}'),
|
|
26
|
+
Endpoint("dashboards.record-view-options", "flowbase_dashboards_record_view_options", "GET", "/dashboards/record-view-options", "metadata:read", "Discover draft objects and fields for record-view components.", "flowbase dashboards record-view-options"),
|
|
27
|
+
Endpoint("dashboards.record-view-preview", "flowbase_dashboards_record_view_preview", "POST", "/dashboards/{dashboard_id}/record-view-preview", "metadata:read", "Preview an unsaved record-view component; also requires records:read. No writes; current Host data and draft config.", "flowbase dashboards record-view-preview dsh_xxx --json-file preview.json", request_body='{"component":{"type":"record_view","title":"Orders","layout":{"x":0,"y":0,"w":6,"h":10},"recordView":{"objectId":"obj_xxx","match":"all","filters":[],"fieldIds":["fld_xxx"],"pageSize":10,"showSearch":true,"followFilters":true}},"filters":[],"filterValues":{},"page":1,"q":""}'),
|
|
28
|
+
Endpoint("dashboards.records", "flowbase_dashboards_records", "POST", "/dashboards/{dashboard_id}/components/{component_id}/records", "metadata:read", "Query a saved draft record-view component; also requires records:read. No writes; not employee workspace permissions.", "flowbase dashboards records dsh_xxx dcm_xxx --page 1 --json-file filters.json", request_body='{"filterValues":{},"page":1,"q":""}'),
|
|
29
|
+
Endpoint("iframes.list", "flowbase_iframes_list", "GET", "/iframes", "metadata:read", "List iframe configuration.", "flowbase iframes list", requires_confirmation=False, request_body=None),
|
|
30
|
+
Endpoint("iframes.get", "flowbase_iframes_get", "GET", "/iframes/{iframe_id}", "metadata:read", "Get iframe configuration.", "flowbase iframes get", requires_confirmation=False, request_body=None),
|
|
31
|
+
Endpoint("iframes.create", "flowbase_iframes_create", "POST", "/iframes", "metadata:write", "Create iframe configuration.", "flowbase iframes create", requires_confirmation=False, request_body='{"label": "Help", "url": "https://example.com", "icon": "IconWorld", "color": "#2563EB"}'),
|
|
32
|
+
Endpoint("iframes.update", "flowbase_iframes_update", "PUT", "/iframes/{iframe_id}", "metadata:write", "Update iframe configuration.", "flowbase iframes update", requires_confirmation=False, request_body='{"label": "Help", "url": "https://example.com", "icon": "IconWorld", "color": "#2563EB", "baseRevision": 0}'),
|
|
33
|
+
Endpoint("iframes.delete", "flowbase_iframes_delete", "POST", "/iframes/{iframe_id}/delete", "metadata:write", "Delete iframe configuration.", "flowbase iframes delete", requires_confirmation=True, request_body='{"baseRevision": 0}'),
|
|
34
|
+
Endpoint(
|
|
35
|
+
"apps.iframes.add",
|
|
36
|
+
"flowbase_apps_iframes_add",
|
|
37
|
+
"POST",
|
|
38
|
+
"/apps/{app_id}/iframes",
|
|
39
|
+
"metadata:write",
|
|
40
|
+
"Add iframes to an app (association only).",
|
|
41
|
+
"flowbase apps add-iframes app_xxx ifr_xxx",
|
|
42
|
+
request_body='{"iframeIds":["ifr_xxx"]}',
|
|
43
|
+
),
|
|
44
|
+
Endpoint(
|
|
45
|
+
"apps.iframes.remove",
|
|
46
|
+
"flowbase_apps_iframes_remove",
|
|
47
|
+
"DELETE",
|
|
48
|
+
"/apps/{app_id}/iframes/{iframe_id}",
|
|
49
|
+
"metadata:write",
|
|
50
|
+
"Remove a iframe association from an app.",
|
|
51
|
+
"flowbase apps remove-iframe app_xxx ifr_xxx --yes",
|
|
52
|
+
True,
|
|
53
|
+
),
|
|
54
|
+
Endpoint(
|
|
55
|
+
"apps.iframes.set-group",
|
|
56
|
+
"flowbase_apps_iframes_set_group",
|
|
57
|
+
"POST",
|
|
58
|
+
"/apps/{app_id}/iframes/{iframe_id}/group",
|
|
59
|
+
"metadata:write",
|
|
60
|
+
"Move an app-linked iframe into a group or ungroup it.",
|
|
61
|
+
"flowbase apps set-iframe-group app_xxx ifr_xxx --group-id grp_xxx",
|
|
62
|
+
request_body='{"groupId":"grp_xxx"}',
|
|
63
|
+
),
|
|
64
|
+
|
|
25
65
|
Endpoint('formulas.sources', 'flowbase_formulas_sources', 'GET', '/objects/{object_id}/formula/sources', 'metadata:read', 'Discover scalar formula sources in the draft.', 'flowbase formulas sources obj_xxx'),
|
|
26
66
|
Endpoint('formulas.collections', 'flowbase_formulas_collections', 'GET', '/objects/{object_id}/formula/collections', 'metadata:read', 'Discover collection object and field IDs in the draft.', 'flowbase formulas collections obj_xxx'),
|
|
27
67
|
Endpoint('formulas.validate', 'flowbase_formulas_validate', 'POST', '/objects/{object_id}/formula/validate', 'metadata:read', 'Validate formula or rollup; trial also supported. Requires records:read. No writes.', 'flowbase formulas validate obj_xxx --json-file formula.json', request_body='{"constraints":{"expression":"amount * 2","resultType":"number","scale":2},"samples":{"amount":12.50},"trial":true}'),
|
|
@@ -52,7 +92,7 @@ ENDPOINTS = (
|
|
|
52
92
|
"POST",
|
|
53
93
|
"/apps/{app_id}/groups",
|
|
54
94
|
"metadata:write",
|
|
55
|
-
"Create an app group draft.",
|
|
95
|
+
"Create an app group draft; optional sectionId selects a section (omitted means unassigned).",
|
|
56
96
|
"flowbase groups create app_xxx --name Sales --icon IconFolder",
|
|
57
97
|
request_body='{"name":"Sales","icon":"IconFolder"}',
|
|
58
98
|
),
|
|
@@ -258,7 +298,7 @@ ENDPOINTS = (
|
|
|
258
298
|
'{"fieldId":"fld_done","operator":"is_true"}'
|
|
259
299
|
'],"defaultFieldIds":["fld_a","fld_b"],"groupFieldId":null,"displayMode":"table","parentFieldId":null,"gantt":{}}'
|
|
260
300
|
)),
|
|
261
|
-
Endpoint("views.update", "flowbase_views_update", "PUT", "/objects/{object_id}/views/{view_id}", "metadata:write", "Update a complete view snapshot. Preserve displayMode, parentFieldId and
|
|
301
|
+
Endpoint("views.update", "flowbase_views_update", "PUT", "/objects/{object_id}/views/{view_id}", "metadata:write", "Update a complete view snapshot. Preserve displayMode, parentFieldId, gantt and custom from views.list; Read custom-views catalog for JSX configuration; omitted fields use defaults.", "flowbase views update obj_xxx viw_xxx --json-file view.json",
|
|
262
302
|
request_body=(
|
|
263
303
|
'{"baseRevision":4,"name":"Active","enabled":true,"match":"all",'
|
|
264
304
|
'"filters":['
|
|
@@ -1040,7 +1080,7 @@ APPROVAL_NODE_TYPES = (
|
|
|
1040
1080
|
)
|
|
1041
1081
|
|
|
1042
1082
|
FUNCTION_PERMISSIONS = (
|
|
1043
|
-
"workspace.access", "app.manage", "object.manage", "dashboard.manage", "form.manage",
|
|
1083
|
+
"workspace.access", "app.manage", "object.manage", "dashboard.manage", "form.manage", "iframe.manage",
|
|
1044
1084
|
"version.manage", "org.manage", "system.manage",
|
|
1045
1085
|
)
|
|
1046
1086
|
|
|
@@ -1088,7 +1128,7 @@ PAGE_COMPONENTS = {
|
|
|
1088
1128
|
),
|
|
1089
1129
|
"detailOnlyKinds": (
|
|
1090
1130
|
"relatedList", "approval", "detailSummary", "stageBar", "tabs",
|
|
1091
|
-
"relatedSummary", "attachmentCard", "noticeBlock",
|
|
1131
|
+
"relatedSummary", "attachmentCard", "noticeBlock", "recordEmbed",
|
|
1092
1132
|
),
|
|
1093
1133
|
},
|
|
1094
1134
|
"kinds": (
|
|
@@ -1207,7 +1247,7 @@ PAGE_COMPONENTS = {
|
|
|
1207
1247
|
"minimumComponents": 1,
|
|
1208
1248
|
"allowedChildKinds": (
|
|
1209
1249
|
"field", "relatedList", "approval", "stageBar",
|
|
1210
|
-
"relatedSummary", "attachmentCard", "noticeBlock",
|
|
1250
|
+
"relatedSummary", "attachmentCard", "noticeBlock", "recordEmbed",
|
|
1211
1251
|
),
|
|
1212
1252
|
},
|
|
1213
1253
|
"note": "defaultTabId must reference a tab in this component; tabs cannot be nested.",
|
|
@@ -1248,6 +1288,22 @@ PAGE_COMPONENTS = {
|
|
|
1248
1288
|
"titleLengthWhenShown": (1, 50),
|
|
1249
1289
|
},
|
|
1250
1290
|
},
|
|
1291
|
+
{
|
|
1292
|
+
"kind": "recordEmbed",
|
|
1293
|
+
"name": "record detail embedded page",
|
|
1294
|
+
"pageTypes": ("detail",),
|
|
1295
|
+
"placement": ("topLevel", "tabs"),
|
|
1296
|
+
"requiredKeys": ("kind", "label", "url"),
|
|
1297
|
+
"optionalKeys": ("id", "showTitle", "height", "allowRead", "allowedFieldIds"),
|
|
1298
|
+
"defaults": {"showTitle": True, "height": 480, "allowRead": False, "allowedFieldIds": []},
|
|
1299
|
+
"constraints": {
|
|
1300
|
+
"labelLength": (1, 80),
|
|
1301
|
+
"height": "integer, 240–1600",
|
|
1302
|
+
"url": "External HTTPS only; no credentials, identity/context parameters, templates, localhost or FlowBase hosts.",
|
|
1303
|
+
"allowedFieldIds": "Unique active field IDs on the Page object; at least one when allowRead=true. Runtime reads also enforce current record and field permissions.",
|
|
1304
|
+
},
|
|
1305
|
+
"runtime": "Browser SDK /sdk/record-embed.v1.js; read-only bridge. CLI authors configuration and does not render or execute the embed.",
|
|
1306
|
+
},
|
|
1251
1307
|
{
|
|
1252
1308
|
"kind": "noticeBlock",
|
|
1253
1309
|
"name": "notice block",
|
|
@@ -1323,7 +1379,7 @@ PAGE_RULES = {
|
|
|
1323
1379
|
"states": ("visible", "hidden"),
|
|
1324
1380
|
"targetKinds": (
|
|
1325
1381
|
"relatedList", "approval", "detailSummary", "stageBar", "tabs",
|
|
1326
|
-
"relatedSummary", "attachmentCard", "noticeBlock",
|
|
1382
|
+
"relatedSummary", "attachmentCard", "noticeBlock", "recordEmbed",
|
|
1327
1383
|
),
|
|
1328
1384
|
"excludedTargets": ("field", "region", "tab", "button", "systemArea"),
|
|
1329
1385
|
"sameRuleUniqueTarget": True,
|
|
@@ -1337,7 +1393,7 @@ PAGE_RULES = {
|
|
|
1337
1393
|
}
|
|
1338
1394
|
|
|
1339
1395
|
RESOURCE_ID_PREFIXES = {
|
|
1340
|
-
"app_": "app", "grp_": "app group", "obj_": "object", "fld_": "field",
|
|
1396
|
+
"app_": "app", "grp_": "app group", "sec_": "app section", "obj_": "object", "fld_": "field",
|
|
1341
1397
|
"viw_": "view", "wfl_": "workflow", "apr_": "approval flow", "pag_": "custom page",
|
|
1342
1398
|
"reg_": "page region", "cmp_": "page component",
|
|
1343
1399
|
"dsh_": "dashboard", "dcm_": "dashboard component", "rol_": "role",
|
|
@@ -1542,6 +1598,37 @@ FORMS = {
|
|
|
1542
1598
|
}
|
|
1543
1599
|
|
|
1544
1600
|
CATALOGS = {
|
|
1601
|
+
"app-sections": {
|
|
1602
|
+
"read": "apps get APP returns sections and sectionId on groups/objects/dashboards/iframes. Null means unassigned; groups define their members' section.",
|
|
1603
|
+
"write": "apps navigation APP --json-file navigation.json --yes writes one atomic draft operation and returns id/draftRevision. Read current locations first; stale moves fail without changes.",
|
|
1604
|
+
"operations": [
|
|
1605
|
+
{"type": "appSection.create", "name": "Sales"},
|
|
1606
|
+
{"type": "appSection.update", "id": "sec_xxx", "name": "Revenue"},
|
|
1607
|
+
{"type": "appSection.delete", "id": "sec_xxx"},
|
|
1608
|
+
{"type": "appSection.reorder", "sectionIds": ["sec_second", "sec_first"]},
|
|
1609
|
+
{"type": "appGroup.moveToSection", "id": "grp_xxx", "sectionId": "sec_target", "expectedSectionId": None},
|
|
1610
|
+
{"type": "appMember.move", "id": "obj_xxx", "sectionId": "sec_target", "groupId": None, "expectedSectionId": None, "expectedGroupId": None},
|
|
1611
|
+
],
|
|
1612
|
+
"rules": "Names are trimmed, 1-50 characters, case-insensitively unique; Unassigned/未分栏 are reserved. Only empty sections can be deleted. Reorder supplies all section IDs. Moves require explicit source/target fields, using null for unassigned/ungrouped; target group must belong to target section. Member id may be obj_/dsh_/ifr_.",
|
|
1613
|
+
"add": "groups create and apps add-objects/add-dashboards/add-iframes accept --section-id; omit for unassigned. Existing associations are not moved by add; use navigation.",
|
|
1614
|
+
"order": "Use groups reorder or apps reorder-members for order within a location; preserve the complete app-level order and other sections/groups from apps get.",
|
|
1615
|
+
},
|
|
1616
|
+
"dashboard-record-views": {
|
|
1617
|
+
"discover": "dashboards record-view-options returns draft source object and field IDs; dashboards get returns components and draftRevision.",
|
|
1618
|
+
"component": {"type": "record_view", "title": "Orders", "layout": {"x": 0, "y": 0, "w": 6, "h": 10}, "recordView": {"objectId": "obj_xxx", "match": "all", "filters": [], "fieldIds": ["fld_xxx"], "pageSize": 10, "showSearch": True, "followFilters": True}},
|
|
1619
|
+
"update": "dashboards update is a full snapshot: copy label/description/icon/filters/components from get, preserve all components and their IDs (including code/recordView), set baseRevision from draftRevision. Omit response-only id/key/componentCount/appIds/updatedAt/draftRevision. New components omit id; save allocates it. Omitting components or filters clears them.",
|
|
1620
|
+
"query": "dashboards records DASHBOARD COMPONENT [--query TEXT] [--page N] --json '{\"filterValues\":{}}' queries a saved draft component. dashboards record-view-preview DASHBOARD --json-file preview.json uses unsaved component and filters; docs example dashboards.record-view-preview supplies the body.",
|
|
1621
|
+
"permissions": "Queries require metadata:read and records:read and use current Host data with draft config (mode=draft-preview). API-key identity, not employee workspace access; hidden fields are excluded, hidden filter fields rejected. No writes or app association required.",
|
|
1622
|
+
"rules": "pageSize is 10/20/50; fieldIds are ordered stable field IDs. Fixed all/any conditions intersect dashboard filters and search. showSearch=false ignores q; followFilters=false ignores dashboard filters. Conditions use view-filters catalog. No object-view dependency. Code components use dashboards run.",
|
|
1623
|
+
"response": "columns/records/total/page/pageSize/objectId/title/showSearch/filterValues/conditions/match/mode/draftRevision; resolved absolute dates are returned in filterValues. Iterate until total is covered.",
|
|
1624
|
+
},
|
|
1625
|
+
"custom-views": {
|
|
1626
|
+
"commands": "views list/create/update/set-enabled/delete; create/update accept --json-file.",
|
|
1627
|
+
"example": {"baseRevision": 4, "name": "Custom", "enabled": False, "defaultFieldIds": [], "displayMode": "custom", "custom": {"runtimeVersion": "cv1", "source": "import React from 'react'; export default function View(){return <div>Hello</div>}"}},
|
|
1628
|
+
"update": "Read views.list; preserve custom.source, custom.runtimeVersion, displayMode and the other writable snapshot fields. Use draftRevision as baseRevision; omit response-only id and status from the body.",
|
|
1629
|
+
"constraints": "Custom views start disabled; enable separately after validation. Built-in and custom views cannot convert into each other. custom contains exactly source and runtimeVersion=cv1; source must be nonempty and at most 512 KiB UTF-8.",
|
|
1630
|
+
"runtime": "Sandboxed browser JSX, with react/three/flowbase imports. CLI saves configuration; browser preview/read bridge is not exposed as an API-key CLI command.",
|
|
1631
|
+
},
|
|
1545
1632
|
"planning": {
|
|
1546
1633
|
"viewUpdate": "Read views.list, preserve the complete snapshot, then views.update with baseRevision. Omitting displayMode/parentFieldId/gantt resets them to table/null/{}.",
|
|
1547
1634
|
"tree": {"displayMode": "tree", "parentFieldId": "fld_parent", "gantt": {}},
|
|
@@ -6,6 +6,8 @@ GUIDE = {
|
|
|
6
6
|
"workflow": [
|
|
7
7
|
"先确认 --host / FLOWBASE_HOST 和 API Key;环境只由 Host 决定。",
|
|
8
8
|
"flowbase apps list;flowbase objects list:发现已有应用和对象,优先复用,不猜测 ID。",
|
|
9
|
+
"应用分栏先 apps get 和 docs catalogs app-sections;apps navigation --json-file --yes 修改分栏/位置,添加内容和创建分组可指定 --section-id。",
|
|
10
|
+
"仪表盘记录视图先 dashboards record-view-options 和 docs catalogs dashboard-record-views;record-view-preview 预览未保存配置,records 查询已保存草稿,run 仅运行代码组件。",
|
|
9
11
|
"flowbase objects get <object_id>:读取字段 key、类型、约束和真实选项 ID;关联字段继续读取目标对象。",
|
|
10
12
|
"flowbase docs catalogs authoring;flowbase docs list;flowbase docs show <endpoint_key>:查请求和更新方式。",
|
|
11
13
|
"配置页面前读取 pages options、page-components、page-rules、page-buttons;配置流程前读取 workflow-nodes、workflow-branches 或 approval-rules;配置打印模板前读取 print-templates;配置自定义表单前读取 forms。",
|
|
@@ -376,6 +376,9 @@ def _payload_from_args(args: argparse.Namespace, direct: dict[str, Any], require
|
|
|
376
376
|
|
|
377
377
|
def handle_apps(args: argparse.Namespace) -> int:
|
|
378
378
|
action = args.apps_action
|
|
379
|
+
if action == "navigation":
|
|
380
|
+
body = _payload_from_args(args, {}, ("type",))
|
|
381
|
+
return run_request(args, "apps.navigation", path_params={"app_id": args.app_id}, body=body)
|
|
379
382
|
if action == "list":
|
|
380
383
|
return run_request(args, "apps.list")
|
|
381
384
|
if action == "get":
|
|
@@ -409,7 +412,7 @@ def handle_apps(args: argparse.Namespace) -> int:
|
|
|
409
412
|
args,
|
|
410
413
|
"apps.objects.add",
|
|
411
414
|
path_params={"app_id": args.app_id},
|
|
412
|
-
body={"objectIds": ids},
|
|
415
|
+
body={"objectIds": ids, **({"sectionId": args.section_id} if args.section_id is not None else {})},
|
|
413
416
|
)
|
|
414
417
|
if action == "remove-object":
|
|
415
418
|
return run_request(
|
|
@@ -446,7 +449,7 @@ def handle_apps(args: argparse.Namespace) -> int:
|
|
|
446
449
|
args,
|
|
447
450
|
"apps.dashboards.add",
|
|
448
451
|
path_params={"app_id": args.app_id},
|
|
449
|
-
body={"dashboardIds": ids},
|
|
452
|
+
body={"dashboardIds": ids, **({"sectionId": args.section_id} if args.section_id is not None else {})},
|
|
450
453
|
)
|
|
451
454
|
if action == "remove-dashboard":
|
|
452
455
|
return run_request(
|
|
@@ -467,6 +470,35 @@ def handle_apps(args: argparse.Namespace) -> int:
|
|
|
467
470
|
path_params={"app_id": args.app_id, "dashboard_id": args.dashboard_id},
|
|
468
471
|
body=body,
|
|
469
472
|
)
|
|
473
|
+
if action == "add-iframes":
|
|
474
|
+
ids = [item.strip() for item in args.iframe_ids if item.strip()]
|
|
475
|
+
if not ids:
|
|
476
|
+
raise CliError("Provide at least one iframe ID.")
|
|
477
|
+
return run_request(
|
|
478
|
+
args,
|
|
479
|
+
"apps.iframes.add",
|
|
480
|
+
path_params={"app_id": args.app_id},
|
|
481
|
+
body={"iframeIds": ids, **({"sectionId": args.section_id} if args.section_id is not None else {})},
|
|
482
|
+
)
|
|
483
|
+
if action == "remove-iframe":
|
|
484
|
+
return run_request(
|
|
485
|
+
args,
|
|
486
|
+
"apps.iframes.remove",
|
|
487
|
+
path_params={"app_id": args.app_id, "iframe_id": args.iframe_id},
|
|
488
|
+
)
|
|
489
|
+
if action == "set-iframe-group":
|
|
490
|
+
if args.ungrouped:
|
|
491
|
+
body = {"groupId": None}
|
|
492
|
+
elif args.group_id is not None:
|
|
493
|
+
body = {"groupId": args.group_id}
|
|
494
|
+
else:
|
|
495
|
+
raise CliError("Provide --group-id or --ungrouped.")
|
|
496
|
+
return run_request(
|
|
497
|
+
args,
|
|
498
|
+
"apps.iframes.set-group",
|
|
499
|
+
path_params={"app_id": args.app_id, "iframe_id": args.iframe_id},
|
|
500
|
+
body=body,
|
|
501
|
+
)
|
|
470
502
|
if action == "reorder-members":
|
|
471
503
|
body = _payload_from_args(args, {}, ("items",))
|
|
472
504
|
if not isinstance(body["items"], list):
|
|
@@ -482,6 +514,14 @@ def handle_apps(args: argparse.Namespace) -> int:
|
|
|
482
514
|
|
|
483
515
|
def handle_dashboards(args: argparse.Namespace) -> int:
|
|
484
516
|
action = args.dashboards_action
|
|
517
|
+
if action == "record-view-options":
|
|
518
|
+
return run_request(args, "dashboards.record-view-options")
|
|
519
|
+
if action in {"records", "record-view-preview"}:
|
|
520
|
+
body = _payload_from_args(args, {"q": args.query, "page": args.page}, ("component",) if action == "record-view-preview" else ())
|
|
521
|
+
path = {"dashboard_id": args.dashboard_id}
|
|
522
|
+
if action == "records":
|
|
523
|
+
path["component_id"] = args.component_id
|
|
524
|
+
return run_request(args, f"dashboards.{action}", path_params=path, body=body)
|
|
485
525
|
if action == "run":
|
|
486
526
|
return run_request(args, "dashboards.run", path_params={"dashboard_id": args.dashboard_id, "component_id": args.component_id}, body=load_json_payload(args.json, args.json_file))
|
|
487
527
|
if action == "list":
|
|
@@ -522,6 +562,13 @@ def handle_dashboards(args: argparse.Namespace) -> int:
|
|
|
522
562
|
)
|
|
523
563
|
|
|
524
564
|
|
|
565
|
+
def handle_iframes(args: argparse.Namespace) -> int:
|
|
566
|
+
action = args.iframes_action
|
|
567
|
+
params = {"iframe_id": args.iframe_id} if action in ("get", "update", "delete") else None
|
|
568
|
+
body = load_json_payload(args.json, args.json_file) if action in ("create", "update", "delete") else None
|
|
569
|
+
return run_request(args, f"iframes.{action}", path_params=params, body=body)
|
|
570
|
+
|
|
571
|
+
|
|
525
572
|
def handle_forms(args: argparse.Namespace) -> int:
|
|
526
573
|
action = args.forms_action
|
|
527
574
|
if action == "list":
|
|
@@ -560,7 +607,7 @@ def handle_groups(args: argparse.Namespace) -> int:
|
|
|
560
607
|
action = args.groups_action
|
|
561
608
|
path = {"app_id": args.app_id}
|
|
562
609
|
if action == "create":
|
|
563
|
-
body = _payload_from_args(args, {"name": args.name, "icon": args.icon}, ("name",))
|
|
610
|
+
body = _payload_from_args(args, {"name": args.name, "icon": args.icon, "sectionId": args.section_id}, ("name",))
|
|
564
611
|
return run_request(args, "apps.groups.create", path_params=path, body=body)
|
|
565
612
|
if action == "reorder":
|
|
566
613
|
body = _payload_from_args(args, {}, ("groupIds",))
|
|
@@ -1100,6 +1147,10 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1100
1147
|
app_actions.add_parser("list")
|
|
1101
1148
|
app_get = app_actions.add_parser("get")
|
|
1102
1149
|
app_get.add_argument("app_id")
|
|
1150
|
+
app_navigation = app_actions.add_parser("navigation", help="Edit sections or move content; see docs catalogs app-sections.")
|
|
1151
|
+
app_navigation.add_argument("app_id")
|
|
1152
|
+
add_json_args(app_navigation, endpoint_key="apps.navigation")
|
|
1153
|
+
add_yes(app_navigation)
|
|
1103
1154
|
app_create = app_actions.add_parser("create")
|
|
1104
1155
|
app_create.add_argument("--label")
|
|
1105
1156
|
app_create.add_argument("--key")
|
|
@@ -1126,6 +1177,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1126
1177
|
app_add_objects = app_actions.add_parser("add-objects")
|
|
1127
1178
|
app_add_objects.add_argument("app_id")
|
|
1128
1179
|
app_add_objects.add_argument("--object-ids", required=True, help="Comma-separated object IDs.")
|
|
1180
|
+
app_add_objects.add_argument("--section-id", help="Target section; omitted means unassigned.")
|
|
1129
1181
|
app_remove_object = app_actions.add_parser("remove-object")
|
|
1130
1182
|
app_remove_object.add_argument("app_id")
|
|
1131
1183
|
app_remove_object.add_argument("object_id")
|
|
@@ -1144,6 +1196,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1144
1196
|
add_json_args(app_reorder_objects, endpoint_key="apps.objects.reorder")
|
|
1145
1197
|
app_add_dashboards = app_actions.add_parser("add-dashboards")
|
|
1146
1198
|
app_add_dashboards.add_argument("app_id")
|
|
1199
|
+
app_add_dashboards.add_argument("--section-id", help="Target section; omitted means unassigned.")
|
|
1147
1200
|
app_add_dashboards.add_argument(
|
|
1148
1201
|
"dashboard_ids",
|
|
1149
1202
|
nargs="+",
|
|
@@ -1162,6 +1215,27 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1162
1215
|
action="store_true",
|
|
1163
1216
|
help="Remove the dashboard from its current group in this app.",
|
|
1164
1217
|
)
|
|
1218
|
+
app_add_iframes = app_actions.add_parser("add-iframes")
|
|
1219
|
+
app_add_iframes.add_argument("app_id")
|
|
1220
|
+
app_add_iframes.add_argument("--section-id", help="Target section; omitted means unassigned.")
|
|
1221
|
+
app_add_iframes.add_argument(
|
|
1222
|
+
"iframe_ids",
|
|
1223
|
+
nargs="+",
|
|
1224
|
+
help="One or more iframe IDs to associate with the app.",
|
|
1225
|
+
)
|
|
1226
|
+
app_remove_iframe = app_actions.add_parser("remove-iframe")
|
|
1227
|
+
app_remove_iframe.add_argument("app_id")
|
|
1228
|
+
app_remove_iframe.add_argument("iframe_id")
|
|
1229
|
+
add_yes(app_remove_iframe)
|
|
1230
|
+
app_set_iframe_group = app_actions.add_parser("set-iframe-group")
|
|
1231
|
+
app_set_iframe_group.add_argument("app_id")
|
|
1232
|
+
app_set_iframe_group.add_argument("iframe_id")
|
|
1233
|
+
app_set_iframe_group.add_argument("--group-id", help="Target group ID. Omit with --ungrouped.")
|
|
1234
|
+
app_set_iframe_group.add_argument(
|
|
1235
|
+
"--ungrouped",
|
|
1236
|
+
action="store_true",
|
|
1237
|
+
help="Remove the iframe from its current group in this app.",
|
|
1238
|
+
)
|
|
1165
1239
|
app_reorder_members = app_actions.add_parser("reorder-members")
|
|
1166
1240
|
app_reorder_members.add_argument("app_id")
|
|
1167
1241
|
add_json_args(app_reorder_members, endpoint_key="apps.members.reorder")
|
|
@@ -1169,6 +1243,15 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1169
1243
|
|
|
1170
1244
|
dashboards = groups.add_parser("dashboards", help="Manage dashboard metadata.")
|
|
1171
1245
|
dashboard_actions = dashboards.add_subparsers(dest="dashboards_action", required=True)
|
|
1246
|
+
dashboard_actions.add_parser("record-view-options", help="Discover draft objects and fields for record-view components.")
|
|
1247
|
+
for action in ("records", "record-view-preview"):
|
|
1248
|
+
command = dashboard_actions.add_parser(action, help="Read-only draft record-view query; requires metadata:read and records:read.")
|
|
1249
|
+
command.add_argument("dashboard_id")
|
|
1250
|
+
if action == "records":
|
|
1251
|
+
command.add_argument("component_id")
|
|
1252
|
+
command.add_argument("--query", help="Component search text; ignored when showSearch is false.")
|
|
1253
|
+
command.add_argument("--page", type=int, help="Page number, starting at 1.")
|
|
1254
|
+
add_json_args(command, endpoint_key=f"dashboards.{action}")
|
|
1172
1255
|
dashboard_run = dashboard_actions.add_parser("run", help="Run a saved draft component; requires metadata:read and records:read.")
|
|
1173
1256
|
dashboard_run.add_argument("dashboard_id")
|
|
1174
1257
|
dashboard_run.add_argument("component_id")
|
|
@@ -1192,6 +1275,18 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1192
1275
|
add_yes(dashboard_delete)
|
|
1193
1276
|
dashboards.set_defaults(handler=handle_dashboards)
|
|
1194
1277
|
|
|
1278
|
+
iframes = groups.add_parser("iframes", help="Manage reusable iframe configurations.")
|
|
1279
|
+
iframe_actions = iframes.add_subparsers(dest="iframes_action", required=True)
|
|
1280
|
+
for action in ("list", "get", "create", "update", "delete"):
|
|
1281
|
+
command = iframe_actions.add_parser(action)
|
|
1282
|
+
if action in ("get", "update", "delete"):
|
|
1283
|
+
command.add_argument("iframe_id")
|
|
1284
|
+
if action in ("create", "update", "delete"):
|
|
1285
|
+
add_json_args(command, endpoint_key=f"iframes.{action}")
|
|
1286
|
+
if action == "delete":
|
|
1287
|
+
add_yes(command)
|
|
1288
|
+
iframes.set_defaults(handler=handle_iframes)
|
|
1289
|
+
|
|
1195
1290
|
forms = groups.add_parser("forms", help="Manage custom forms (public collection links).")
|
|
1196
1291
|
form_actions = forms.add_subparsers(dest="forms_action", required=True)
|
|
1197
1292
|
form_actions.add_parser("list")
|
|
@@ -1229,6 +1324,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1229
1324
|
group_create.add_argument("app_id")
|
|
1230
1325
|
group_create.add_argument("--name")
|
|
1231
1326
|
group_create.add_argument("--icon")
|
|
1327
|
+
group_create.add_argument("--section-id", help="Target section; omitted means unassigned.")
|
|
1232
1328
|
add_json_args(group_create, endpoint_key="apps.groups.create")
|
|
1233
1329
|
group_reorder = group_actions.add_parser("reorder")
|
|
1234
1330
|
group_reorder.add_argument("app_id")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flowbase-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.17
|
|
4
4
|
Summary: Command line client for the FlowBase OpenAPI
|
|
5
5
|
Author: DeepFlowAI
|
|
6
6
|
License: Apache-2.0
|
|
@@ -76,7 +76,7 @@ flowbase
|
|
|
76
76
|
├── docs list | show | example | operations Offline endpoint catalog
|
|
77
77
|
├── guide Offline product and task guide (--format markdown|json)
|
|
78
78
|
├── organization employees | departments
|
|
79
|
-
├── apps list | get | create | update | set-enabled | delete
|
|
79
|
+
├── apps list | get | create | update | set-enabled | delete | navigation
|
|
80
80
|
│ add-objects | remove-object | set-group | reorder-objects
|
|
81
81
|
│ add-dashboards | remove-dashboard | set-dashboard-group | reorder-members
|
|
82
82
|
├── groups create | reorder | update | delete App groups (under an app)
|
|
@@ -88,6 +88,7 @@ flowbase
|
|
|
88
88
|
├── approvals list | get | create | update | reorder | set-enabled | delete
|
|
89
89
|
├── pages list | options | get | create | update | set-default | clear-default | delete | restore | evaluate
|
|
90
90
|
├── dashboards list | get | create | update | copy | delete | run
|
|
91
|
+
│ record-view-options | record-view-preview | records
|
|
91
92
|
├── changes commit | release | direct-release
|
|
92
93
|
├── versions list | show | releases Read-only history
|
|
93
94
|
├── environments restart Destructive test-env reset
|
|
@@ -138,7 +139,8 @@ Most `update` commands replace a **full snapshot** and accept an optional `baseR
|
|
|
138
139
|
|
|
139
140
|
```bash
|
|
140
141
|
flowbase dashboards get dsh_xxx > current.json # authoritative snapshot
|
|
141
|
-
#
|
|
142
|
+
# Keep only label/description/icon/filters/components; use draftRevision as baseRevision.
|
|
143
|
+
# Edit the desired fields while preserving all other components and filters.
|
|
142
144
|
flowbase dashboards update dsh_xxx --json-file current.json
|
|
143
145
|
```
|
|
144
146
|
|
|
@@ -160,6 +162,14 @@ flowbase objects cli-edit-policy update obj_xxx --json '{"baseRevision":4,"polic
|
|
|
160
162
|
|
|
161
163
|
`records create` returns `{recordId}`; use it with `records get`.
|
|
162
164
|
|
|
165
|
+
To add existing objects to an application, pass their IDs with `--object-ids`:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
flowbase apps add-objects app_xxx --object-ids obj_a,obj_b
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Use a comma-separated list for multiple objects. Object IDs are not positional arguments. Run `flowbase docs show apps.objects.add` for the endpoint details.
|
|
172
|
+
|
|
163
173
|
Record `values` keys are **field keys** (`name`), not internal IDs (`fld_...`). IDs are `obj_`/`fld_`/`dsh_`/`wfl_`/`pag_`/`reg_`/`cmp_`-prefixed; see `docs catalogs id-prefixes`.
|
|
164
174
|
|
|
165
175
|
### 6.2 Edit and publish configuration on a test environment
|
|
@@ -178,6 +188,31 @@ flowbase changes release --message "Ship sales dashboard" --yes
|
|
|
178
188
|
|
|
179
189
|
Dashboard component code runs server-side in a sandbox and returns ECharts config / metric / table / markdown. Components and filters are validated on save; use `flowbase dashboards get <id>` to discover component IDs, then `flowbase dashboards run <dashboard_id> <component_id>` to inspect `ok/output/error/logs` in a read-only draft preview (requires metadata:read and records:read). Visual chart rendering still requires the web UI.
|
|
180
190
|
|
|
191
|
+
App sections and record-view components:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
flowbase apps get app_xxx
|
|
195
|
+
flowbase docs catalogs app-sections
|
|
196
|
+
flowbase apps navigation app_xxx --json '{"type":"appSection.create","name":"Sales"}' --yes
|
|
197
|
+
flowbase groups create app_xxx --name Orders --section-id sec_xxx
|
|
198
|
+
flowbase apps add-objects app_xxx --object-ids obj_xxx --section-id sec_xxx
|
|
199
|
+
# add-dashboards and add-iframes also accept --section-id.
|
|
200
|
+
flowbase apps navigation app_xxx --json '{"type":"appMember.move","id":"obj_xxx","sectionId":"sec_xxx","groupId":"grp_xxx","expectedSectionId":"sec_xxx","expectedGroupId":null}' --yes
|
|
201
|
+
|
|
202
|
+
flowbase docs catalogs dashboard-record-views
|
|
203
|
+
flowbase dashboards record-view-options
|
|
204
|
+
flowbase docs example dashboards.record-view-preview --output preview.json
|
|
205
|
+
# Replace placeholder IDs and configure component/filters before previewing.
|
|
206
|
+
flowbase dashboards record-view-preview dsh_xxx --json-file preview.json
|
|
207
|
+
flowbase dashboards records dsh_xxx dcm_xxx --page 1 --query Orders --json '{"filterValues":{}}'
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`apps get` preserves section definitions and group/member `sectionId`. Navigation supports section creation, rename, empty-section deletion, full section ordering, group moves and member moves. Moves require explicit source and target positions; use JSON `null` for unassigned/ungrouped. See `app-sections` for all six request bodies. Navigation requires confirmation (`--yes` for automation), and writes only a draft.
|
|
211
|
+
|
|
212
|
+
Record-view previews and queries require both `metadata:read` and `records:read`, use the current Host's records and draft configuration, and return `mode: draft-preview` plus `draftRevision`. They use API-key visibility rather than an employee's workspace identity; hidden fields stay excluded and hidden filter fields are rejected. Page size is configured on the component (10/20/50). Search is ignored when `showSearch` is false. `dashboards run` is for code components.
|
|
213
|
+
|
|
214
|
+
For `dashboards update`, construct the body from the latest `get`: preserve `label`, `description`, `icon`, `filters`, and **all** `components`; set `baseRevision` to the returned `draftRevision`. Omit response-only fields. Keep existing component IDs and `recordView` unchanged when editing other properties; omit IDs on new components. Omitting `components` or `filters` clears them.
|
|
215
|
+
|
|
181
216
|
### 6.3 Production hotfix
|
|
182
217
|
|
|
183
218
|
```bash
|
|
@@ -289,3 +324,68 @@ Transport requests time out after 60 seconds. A lost write response has an unkno
|
|
|
289
324
|
- `flowbase docs catalogs workflow-python`: record creation examples and failed-task retry. `workflows python-runtime OBJECT new` discovers current object bindings; `workflows executions` includes Python attempts, logs and `canRetry`.
|
|
290
325
|
|
|
291
326
|
Use `records list OBJECT --view-id VIEW --view-layout` for tree/Gantt projections; the default remains a flat list. `records update` accepts `updatedAt` and `planViewId` in its JSON body for version-checked plan edits. Formula validation/trials require both metadata read and record read; workflow retry requires both metadata write and record write and explicit CLI confirmation (`--yes` in automation).
|
|
327
|
+
|
|
328
|
+
### Reusable iframe configuration (4.11)
|
|
329
|
+
|
|
330
|
+
`flowbase iframes list|get|create|update|delete` manages environment-specific iframe drafts. Create/update use `--json-file`; fields are `label`, `icon` (default `IconWorld`), `color` (default `#2563EB`), and a complete HTTPS `url` without credentials. Update and delete require `baseRevision` from `iframes get`; deletion also requires confirmation (`--yes` for noninteractive use).
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
flowbase iframes create --json-file iframe.json
|
|
334
|
+
flowbase iframes get ifr_example
|
|
335
|
+
flowbase iframes update ifr_example --json-file iframe-update.json
|
|
336
|
+
flowbase apps add-iframes app_example ifr_example
|
|
337
|
+
flowbase apps set-iframe-group app_example ifr_example --group-id grp_example
|
|
338
|
+
flowbase apps set-iframe-group app_example ifr_example --ungrouped
|
|
339
|
+
flowbase apps reorder-members app_example --json '{"items":[{"type":"iframe","id":"ifr_example"}]}'
|
|
340
|
+
flowbase apps remove-iframe app_example ifr_example --yes
|
|
341
|
+
flowbase iframes delete ifr_example --json '{"baseRevision":12}' --yes
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Removal only unlinks one app. Deletion unlinks all apps atomically. Changes follow the existing commit/release/rollback workflow; they do not fetch, proxy or modify the external website. API keys use `metadata:read`/`metadata:write`; browser roles use `iframe.manage` for shared resources and `app.manage` for app associations.
|
|
345
|
+
|
|
346
|
+
### Custom object views (4.12)
|
|
347
|
+
|
|
348
|
+
Read `flowbase docs catalogs custom-views` and `flowbase views list OBJECT` first. Save the following as `view.json`, replacing `baseRevision` with the current `draftRevision`:
|
|
349
|
+
|
|
350
|
+
```json
|
|
351
|
+
{
|
|
352
|
+
"baseRevision": 4,
|
|
353
|
+
"name": "Custom overview",
|
|
354
|
+
"enabled": false,
|
|
355
|
+
"defaultFieldIds": [],
|
|
356
|
+
"displayMode": "custom",
|
|
357
|
+
"custom": {
|
|
358
|
+
"runtimeVersion": "cv1",
|
|
359
|
+
"source": "import React from 'react'; export default function View(){return <div>Hello</div>}"
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
flowbase views create obj_example --json-file view.json
|
|
366
|
+
flowbase views list obj_example
|
|
367
|
+
flowbase views update obj_example viw_example --json-file view.json
|
|
368
|
+
flowbase views set-enabled obj_example viw_example --base-revision 6 --enabled --yes
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Use the revision returned by the preceding write, rather than the example numbers. Custom views start disabled; preview in the browser and enable when ready. Updates replace the writable snapshot: preserve `custom.source`, `custom.runtimeVersion`, `displayMode`, filters and other configuration from `views list`, excluding the response-only `id` and `status`. Built-in/custom view types cannot be converted. JSX source is limited to 512 KiB UTF-8; the runtime supports `react`, `three` and `flowbase`. CLI configuration does not execute browser previews.
|
|
372
|
+
|
|
373
|
+
### Record detail embedded pages (4.13)
|
|
374
|
+
|
|
375
|
+
`flowbase docs catalogs page-components` documents `recordEmbed`, supported at the top level or inside tabs of a detail Page. Add this component to an existing region column (or a tab's `components`):
|
|
376
|
+
|
|
377
|
+
```json
|
|
378
|
+
{
|
|
379
|
+
"kind": "recordEmbed",
|
|
380
|
+
"label": "External panel",
|
|
381
|
+
"showTitle": true,
|
|
382
|
+
"url": "https://example.com/panel",
|
|
383
|
+
"height": 480,
|
|
384
|
+
"allowRead": false,
|
|
385
|
+
"allowedFieldIds": []
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
Read `flowbase pages get PAGE`, preserve the existing components and IDs, and remove the read-only Page keys listed in `docs catalogs page-components`. Wrap the editable snapshot as `{"baseRevision":4,"page":{...}}`, then use `flowbase pages update PAGE --json-file page.json` (or `pages create --json-file page.json` for a new detail Page). For creation, omit the Page ID and IDs of new regions/components so the server allocates them.
|
|
390
|
+
|
|
391
|
+
Height must be an integer from 240 to 1600. URLs must use external HTTPS and exclude credentials, identity/context parameters and templates. To permit SDK reads, set `allowRead: true` and select at least one unique active field ID in `allowedFieldIds`; current record/field permissions still apply. The browser SDK is `/sdk/record-embed.v1.js`. The CLI configures the component; rendering and SDK calls run in the browser. `flowbase-user` has no metadata management commands.
|
|
@@ -261,7 +261,7 @@ class FlowBaseCliTest(unittest.TestCase):
|
|
|
261
261
|
kinds = {item.get("kind") for item in page_components["kinds"]}
|
|
262
262
|
self.assertEqual(kinds, {
|
|
263
263
|
None, "relatedList", "approval", "detailSummary", "stageBar",
|
|
264
|
-
"tabs", "relatedSummary", "attachmentCard", "noticeBlock",
|
|
264
|
+
"tabs", "relatedSummary", "attachmentCard", "noticeBlock", "recordEmbed",
|
|
265
265
|
})
|
|
266
266
|
approval = next(
|
|
267
267
|
item for item in page_components["kinds"] if item.get("kind") == "approval"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Iframe CLI uses the same API contracts as browser management."""
|
|
2
|
+
import io
|
|
3
|
+
from unittest.mock import patch
|
|
4
|
+
import pytest
|
|
5
|
+
from flowbase_cli.main import main
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.parametrize("args,endpoint,path,body", [
|
|
9
|
+
(["iframes", "list"], "iframes.list", None, None),
|
|
10
|
+
(["iframes", "get", "ifr_x"], "iframes.get", {"iframe_id": "ifr_x"}, None),
|
|
11
|
+
(["iframes", "create", "--json", '{"label":"Help","url":"https://example.com"}'], "iframes.create", None, {"label":"Help","url":"https://example.com"}),
|
|
12
|
+
(["iframes", "update", "ifr_x", "--json", '{"label":"Help","url":"https://example.com","baseRevision":3}'], "iframes.update", {"iframe_id":"ifr_x"}, {"label":"Help","url":"https://example.com","baseRevision":3}),
|
|
13
|
+
(["iframes", "delete", "ifr_x", "--yes", "--json", '{"baseRevision":4}'], "iframes.delete", {"iframe_id":"ifr_x"}, {"baseRevision":4}),
|
|
14
|
+
(["apps", "add-iframes", "app_x", "ifr_x", "ifr_y"], "apps.iframes.add", {"app_id":"app_x"}, {"iframeIds":["ifr_x","ifr_y"]}),
|
|
15
|
+
(["apps", "remove-iframe", "app_x", "ifr_x", "--yes"], "apps.iframes.remove", {"app_id":"app_x","iframe_id":"ifr_x"}, None),
|
|
16
|
+
(["apps", "set-iframe-group", "app_x", "ifr_x", "--ungrouped"], "apps.iframes.set-group", {"app_id":"app_x","iframe_id":"ifr_x"}, {"groupId":None}),
|
|
17
|
+
(["apps", "reorder-members", "app_x", "--json", '{"items":[{"type":"iframe","id":"ifr_x"}]}'], "apps.members.reorder", {"app_id":"app_x"}, {"items":[{"type":"iframe","id":"ifr_x"}]}),
|
|
18
|
+
])
|
|
19
|
+
def test_iframe_cli_request(args, endpoint, path, body):
|
|
20
|
+
with patch("flowbase_cli.main.OpenApiClient.request", return_value=b"{}") as request, patch("sys.stdout", io.StringIO()):
|
|
21
|
+
assert main(args) == 0
|
|
22
|
+
assert request.call_args.args[0] == endpoint
|
|
23
|
+
assert request.call_args.kwargs["path_params"] == path
|
|
24
|
+
assert request.call_args.kwargs["body"] == body
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_delete_requires_confirmation():
|
|
28
|
+
with patch("flowbase_cli.main.OpenApiClient.request") as request, patch("sys.stdin.isatty", return_value=False), patch("sys.stderr", io.StringIO()):
|
|
29
|
+
assert main(["iframes", "delete", "ifr_x", "--json", '{"baseRevision":0}']) == 1
|
|
30
|
+
request.assert_not_called()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|