dwf-platform-cli 0.2.0__py3-none-any.whl
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.
- dwf_cli/__init__.py +3 -0
- dwf_cli/__main__.py +4 -0
- dwf_cli/api/__init__.py +3 -0
- dwf_cli/api/auth.py +46 -0
- dwf_cli/api/client.py +113 -0
- dwf_cli/api/datamodel.py +458 -0
- dwf_cli/api/formmodel.py +197 -0
- dwf_cli/api/funcmodel.py +436 -0
- dwf_cli/cli/__init__.py +69 -0
- dwf_cli/cli/_common.py +152 -0
- dwf_cli/cli/auth.py +197 -0
- dwf_cli/cli/config.py +200 -0
- dwf_cli/cli/datamodel.py +2270 -0
- dwf_cli/cli/formmodel.py +1007 -0
- dwf_cli/cli/funcmodel.py +2055 -0
- dwf_cli/cli/schema.py +210 -0
- dwf_cli/core/__init__.py +0 -0
- dwf_cli/core/config.py +177 -0
- dwf_cli/core/crypto.py +22 -0
- dwf_cli/core/errors.py +63 -0
- dwf_cli/core/output.py +6 -0
- dwf_cli/core/validator.py +129 -0
- dwf_cli/mcp/__init__.py +3 -0
- dwf_cli/mcp/server.py +411 -0
- dwf_cli/schemas/__init__.py +37 -0
- dwf_cli/schemas/datamodel/attribute_bind.schema.json +21 -0
- dwf_cli/schemas/datamodel/attribute_create.schema.json +24 -0
- dwf_cli/schemas/datamodel/attribute_update.schema.json +21 -0
- dwf_cli/schemas/datamodel/create.schema.json +27 -0
- dwf_cli/schemas/datamodel/excel_confirm.schema.json +65 -0
- dwf_cli/schemas/datamodel/external_create.schema.json +47 -0
- dwf_cli/schemas/datamodel/external_update.schema.json +47 -0
- dwf_cli/schemas/datamodel/object_create.schema.json +24 -0
- dwf_cli/schemas/datamodel/object_update.schema.json +17 -0
- dwf_cli/schemas/datamodel/relation_create.schema.json +34 -0
- dwf_cli/schemas/datamodel/relation_update.schema.json +34 -0
- dwf_cli/schemas/datamodel/update.schema.json +26 -0
- dwf_cli/schemas/funcmodel/app_create.schema.json +150 -0
- dwf_cli/schemas/funcmodel/app_update.schema.json +153 -0
- dwf_cli/schemas/funcmodel/language-package_create.schema.json +15 -0
- dwf_cli/schemas/funcmodel/operations_create.schema.json +77 -0
- dwf_cli/schemas/funcmodel/operations_update.schema.json +76 -0
- dwf_platform_cli-0.2.0.dist-info/METADATA +347 -0
- dwf_platform_cli-0.2.0.dist-info/RECORD +47 -0
- dwf_platform_cli-0.2.0.dist-info/WHEEL +4 -0
- dwf_platform_cli-0.2.0.dist-info/entry_points.txt +3 -0
- dwf_platform_cli-0.2.0.dist-info/licenses/LICENSE +190 -0
dwf_cli/cli/formmodel.py
ADDED
|
@@ -0,0 +1,1007 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json as json_mod
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Annotated, Any
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
|
|
11
|
+
from dwf_cli.api import formmodel as formmodel_api
|
|
12
|
+
from dwf_cli.cli._common import (
|
|
13
|
+
get_client,
|
|
14
|
+
get_console,
|
|
15
|
+
handle_error,
|
|
16
|
+
is_tty,
|
|
17
|
+
resolve_format,
|
|
18
|
+
validate_path,
|
|
19
|
+
)
|
|
20
|
+
from dwf_cli.core.errors import ConflictError, DWFError
|
|
21
|
+
from dwf_cli.core.output import OutputFormat
|
|
22
|
+
|
|
23
|
+
_HELP_CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"]}
|
|
24
|
+
|
|
25
|
+
app = typer.Typer(
|
|
26
|
+
help="Form model management.\n\n"
|
|
27
|
+
"Examples:\n"
|
|
28
|
+
" dwf-cli formmodel view list -c User\n"
|
|
29
|
+
" dwf-cli formmodel view list -t standalone\n"
|
|
30
|
+
" dwf-cli formmodel view create -c User -n create --v3-content '{}'",
|
|
31
|
+
context_settings=_HELP_CONTEXT_SETTINGS,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
_view_sub = typer.Typer(
|
|
35
|
+
help="View management.", context_settings=_HELP_CONTEXT_SETTINGS
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
app.add_typer(_view_sub, name="view")
|
|
39
|
+
|
|
40
|
+
DRY_RUN_EXIT_CODE = 10
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ViewType(str, Enum):
|
|
44
|
+
class_ = "class"
|
|
45
|
+
standalone = "standalone"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class FormDeviceType(str, Enum):
|
|
49
|
+
pc = "actPc"
|
|
50
|
+
ipad = "actIpad"
|
|
51
|
+
ipad_h = "actIpadH"
|
|
52
|
+
phone = "actPhone"
|
|
53
|
+
phone_h = "actPhoneH"
|
|
54
|
+
mobile = "actMobile"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class FormType(str, Enum):
|
|
58
|
+
PC = "PC"
|
|
59
|
+
Mobile = "Mobile"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
_STANDALONE_CLASS_NAME = "DWFDesign"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _extract_items(result: Any) -> list[dict[str, Any]]:
|
|
66
|
+
if isinstance(result, list):
|
|
67
|
+
return result
|
|
68
|
+
if isinstance(result, dict):
|
|
69
|
+
payload = result.get("data", result)
|
|
70
|
+
if isinstance(payload, dict):
|
|
71
|
+
items = payload.get("data", payload.get("items", []))
|
|
72
|
+
return items if isinstance(items, list) else []
|
|
73
|
+
if isinstance(payload, list):
|
|
74
|
+
return payload
|
|
75
|
+
return []
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _print_views_table(
|
|
79
|
+
items: list[dict[str, Any]],
|
|
80
|
+
class_name: str,
|
|
81
|
+
) -> None:
|
|
82
|
+
console = get_console()
|
|
83
|
+
cols = list(items[0].keys()) if items else []
|
|
84
|
+
table = Table(
|
|
85
|
+
title=f"Views of [bold]{class_name}[/bold]",
|
|
86
|
+
show_header=True,
|
|
87
|
+
header_style="bold cyan",
|
|
88
|
+
)
|
|
89
|
+
table.add_column("#", style="dim", width=4)
|
|
90
|
+
for col in cols:
|
|
91
|
+
table.add_column(col)
|
|
92
|
+
for idx, item in enumerate(items, 1):
|
|
93
|
+
row = [str(idx)] + [str(item.get(col, "")) for col in cols]
|
|
94
|
+
table.add_row(*row)
|
|
95
|
+
console.print(table)
|
|
96
|
+
console.print(f"[dim]Total {len(items)} views[/dim]")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _read_payload(data: str, file: Path | None) -> Any:
|
|
100
|
+
if file:
|
|
101
|
+
file = validate_path(file)
|
|
102
|
+
return json_mod.loads(file.read_text(encoding="utf-8"))
|
|
103
|
+
if data:
|
|
104
|
+
return json_mod.loads(data)
|
|
105
|
+
raise DWFError("Provide --data or --file")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _build_view_payload(
|
|
109
|
+
class_name: str,
|
|
110
|
+
view_name: str,
|
|
111
|
+
is_relation: bool,
|
|
112
|
+
v3_content: str,
|
|
113
|
+
display_name: str,
|
|
114
|
+
note: str,
|
|
115
|
+
device_type: str,
|
|
116
|
+
form_type: str,
|
|
117
|
+
basic_info: str,
|
|
118
|
+
has_thumbnail: bool,
|
|
119
|
+
widget_annotation: str,
|
|
120
|
+
) -> dict[str, Any]:
|
|
121
|
+
return {
|
|
122
|
+
"className": class_name,
|
|
123
|
+
"viewName": view_name,
|
|
124
|
+
"isRelation": is_relation,
|
|
125
|
+
"v3Content": v3_content,
|
|
126
|
+
"displayName": display_name,
|
|
127
|
+
"note": note,
|
|
128
|
+
"deviceType": device_type,
|
|
129
|
+
"formType": form_type,
|
|
130
|
+
"basicInfo": basic_info,
|
|
131
|
+
"hasThumbnail": has_thumbnail,
|
|
132
|
+
"widgetAnnotation": widget_annotation,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@_view_sub.command(name="list")
|
|
137
|
+
def list_views(
|
|
138
|
+
ctx: typer.Context,
|
|
139
|
+
view_type: Annotated[
|
|
140
|
+
ViewType,
|
|
141
|
+
typer.Option(
|
|
142
|
+
"--type",
|
|
143
|
+
"-t",
|
|
144
|
+
help="View type: class (entity/relation), standalone",
|
|
145
|
+
case_sensitive=False,
|
|
146
|
+
),
|
|
147
|
+
] = ViewType.class_,
|
|
148
|
+
class_name: Annotated[
|
|
149
|
+
str | None,
|
|
150
|
+
typer.Option(
|
|
151
|
+
"--class-name", "-c", help="Class name (required when type is 'class')"
|
|
152
|
+
),
|
|
153
|
+
] = None,
|
|
154
|
+
need_v3_content: Annotated[
|
|
155
|
+
bool,
|
|
156
|
+
typer.Option("--need-v3-content", help="Return form JSON content"),
|
|
157
|
+
] = False,
|
|
158
|
+
fmt: Annotated[
|
|
159
|
+
OutputFormat,
|
|
160
|
+
typer.Option("--format", "-f", help="Output format: table/json"),
|
|
161
|
+
] = OutputFormat.table,
|
|
162
|
+
) -> None:
|
|
163
|
+
try:
|
|
164
|
+
if view_type == ViewType.standalone:
|
|
165
|
+
target_class = _STANDALONE_CLASS_NAME
|
|
166
|
+
elif class_name:
|
|
167
|
+
target_class = class_name
|
|
168
|
+
else:
|
|
169
|
+
raise DWFError("--class-name is required when --type is 'class'")
|
|
170
|
+
|
|
171
|
+
client = get_client(ctx)
|
|
172
|
+
result = formmodel_api.list_views(
|
|
173
|
+
client,
|
|
174
|
+
target_class,
|
|
175
|
+
need_v3_content=need_v3_content,
|
|
176
|
+
)
|
|
177
|
+
items = _extract_items(result)
|
|
178
|
+
|
|
179
|
+
if not items:
|
|
180
|
+
if is_tty():
|
|
181
|
+
get_console().print(f"[dim]No views found for '{target_class}'.[/dim]")
|
|
182
|
+
else:
|
|
183
|
+
typer.echo("[]")
|
|
184
|
+
return
|
|
185
|
+
|
|
186
|
+
fmt = resolve_format(fmt)
|
|
187
|
+
|
|
188
|
+
if fmt == OutputFormat.json:
|
|
189
|
+
typer.echo(json_mod.dumps(result, ensure_ascii=False, indent=2))
|
|
190
|
+
return
|
|
191
|
+
|
|
192
|
+
_print_views_table(items, target_class)
|
|
193
|
+
except DWFError as exc:
|
|
194
|
+
handle_error(exc)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@_view_sub.command(name="create")
|
|
198
|
+
def create_view(
|
|
199
|
+
ctx: typer.Context,
|
|
200
|
+
class_name: Annotated[str, typer.Option("--class-name", "-c", help="Class name")],
|
|
201
|
+
view_name: Annotated[
|
|
202
|
+
str, typer.Option("--view-name", "-n", help="View name (English)")
|
|
203
|
+
],
|
|
204
|
+
v3_content: Annotated[
|
|
205
|
+
str | None,
|
|
206
|
+
typer.Option(
|
|
207
|
+
"--v3-content",
|
|
208
|
+
help="Form JSON content (string)",
|
|
209
|
+
),
|
|
210
|
+
] = None,
|
|
211
|
+
v3_content_file: Annotated[
|
|
212
|
+
Path | None,
|
|
213
|
+
typer.Option(
|
|
214
|
+
"--v3-content-file",
|
|
215
|
+
help="Path to file containing form JSON content",
|
|
216
|
+
),
|
|
217
|
+
] = None,
|
|
218
|
+
display_name: Annotated[
|
|
219
|
+
str, typer.Option("--display-name", help="Display name (Chinese)")
|
|
220
|
+
] = "",
|
|
221
|
+
note: Annotated[str, typer.Option("--note", help="Note")] = "",
|
|
222
|
+
is_relation: Annotated[
|
|
223
|
+
bool,
|
|
224
|
+
typer.Option("--is-relation", help="Whether this is a relation form"),
|
|
225
|
+
] = False,
|
|
226
|
+
device_type: Annotated[
|
|
227
|
+
FormDeviceType,
|
|
228
|
+
typer.Option("--device-type", help="Device type"),
|
|
229
|
+
] = FormDeviceType.pc,
|
|
230
|
+
form_type: Annotated[
|
|
231
|
+
FormType,
|
|
232
|
+
typer.Option("--form-type", help="Form type"),
|
|
233
|
+
] = FormType.PC,
|
|
234
|
+
basic_info: Annotated[str, typer.Option("--basic-info", help="Basic info")] = "",
|
|
235
|
+
has_thumbnail: Annotated[
|
|
236
|
+
bool,
|
|
237
|
+
typer.Option("--has-thumbnail", help="Has thumbnail"),
|
|
238
|
+
] = False,
|
|
239
|
+
widget_annotation: Annotated[
|
|
240
|
+
str, typer.Option("--widget-annotation", help="Widget annotation")
|
|
241
|
+
] = "",
|
|
242
|
+
if_not_exists: Annotated[
|
|
243
|
+
bool,
|
|
244
|
+
typer.Option("--if-not-exists", help="Skip if view already exists"),
|
|
245
|
+
] = False,
|
|
246
|
+
dry_run: Annotated[
|
|
247
|
+
bool,
|
|
248
|
+
typer.Option("--dry-run", help="Preview without executing"),
|
|
249
|
+
] = False,
|
|
250
|
+
) -> None:
|
|
251
|
+
try:
|
|
252
|
+
content = ""
|
|
253
|
+
if v3_content_file:
|
|
254
|
+
v3_content_file = validate_path(v3_content_file)
|
|
255
|
+
content = v3_content_file.read_text(encoding="utf-8")
|
|
256
|
+
elif v3_content:
|
|
257
|
+
content = v3_content
|
|
258
|
+
else:
|
|
259
|
+
if is_tty():
|
|
260
|
+
content = typer.prompt("v3Content (form JSON)")
|
|
261
|
+
else:
|
|
262
|
+
raise DWFError("--v3-content or --v3-content-file is required")
|
|
263
|
+
|
|
264
|
+
payload = _build_view_payload(
|
|
265
|
+
class_name=class_name,
|
|
266
|
+
view_name=view_name,
|
|
267
|
+
is_relation=is_relation,
|
|
268
|
+
v3_content=content,
|
|
269
|
+
display_name=display_name,
|
|
270
|
+
note=note,
|
|
271
|
+
device_type=device_type.value,
|
|
272
|
+
form_type=form_type.value,
|
|
273
|
+
basic_info=basic_info,
|
|
274
|
+
has_thumbnail=has_thumbnail,
|
|
275
|
+
widget_annotation=widget_annotation,
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
if dry_run:
|
|
279
|
+
typer.echo(
|
|
280
|
+
json_mod.dumps(
|
|
281
|
+
{
|
|
282
|
+
"action": "create",
|
|
283
|
+
"resource": "view",
|
|
284
|
+
"data": [payload],
|
|
285
|
+
"if_not_exists": if_not_exists,
|
|
286
|
+
"reversible": False,
|
|
287
|
+
},
|
|
288
|
+
ensure_ascii=False,
|
|
289
|
+
indent=2,
|
|
290
|
+
)
|
|
291
|
+
)
|
|
292
|
+
raise typer.Exit(code=DRY_RUN_EXIT_CODE)
|
|
293
|
+
|
|
294
|
+
client = get_client(ctx)
|
|
295
|
+
result = None
|
|
296
|
+
try:
|
|
297
|
+
result = formmodel_api.create_views(client, [payload])
|
|
298
|
+
typer.echo(f"Created view: {view_name}", err=True)
|
|
299
|
+
except ConflictError:
|
|
300
|
+
if if_not_exists:
|
|
301
|
+
typer.echo(
|
|
302
|
+
f"View already exists, skipped (--if-not-exists): {view_name}",
|
|
303
|
+
err=True,
|
|
304
|
+
)
|
|
305
|
+
else:
|
|
306
|
+
raise
|
|
307
|
+
if isinstance(result, dict):
|
|
308
|
+
typer.echo(json_mod.dumps(result, ensure_ascii=False, indent=2))
|
|
309
|
+
except DWFError as exc:
|
|
310
|
+
handle_error(exc)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
@_view_sub.command(name="update")
|
|
314
|
+
def update_view(
|
|
315
|
+
ctx: typer.Context,
|
|
316
|
+
oid: Annotated[str, typer.Argument(help="View OID to update")],
|
|
317
|
+
class_name: Annotated[str, typer.Option("--class-name", "-c", help="Class name")],
|
|
318
|
+
view_name: Annotated[
|
|
319
|
+
str, typer.Option("--view-name", "-n", help="View name (English)")
|
|
320
|
+
],
|
|
321
|
+
is_relation: Annotated[
|
|
322
|
+
bool,
|
|
323
|
+
typer.Option("--is-relation", help="Whether this is a relation form"),
|
|
324
|
+
] = False,
|
|
325
|
+
v3_content: Annotated[
|
|
326
|
+
str | None,
|
|
327
|
+
typer.Option("--v3-content", help="Form JSON content (string)"),
|
|
328
|
+
] = None,
|
|
329
|
+
v3_content_file: Annotated[
|
|
330
|
+
Path | None,
|
|
331
|
+
typer.Option("--v3-content-file", help="Path to form JSON content file"),
|
|
332
|
+
] = None,
|
|
333
|
+
display_name: Annotated[
|
|
334
|
+
str, typer.Option("--display-name", help="Display name (Chinese)")
|
|
335
|
+
] = "",
|
|
336
|
+
note: Annotated[str, typer.Option("--note", help="Note")] = "",
|
|
337
|
+
device_type: Annotated[
|
|
338
|
+
FormDeviceType,
|
|
339
|
+
typer.Option("--device-type", help="Device type"),
|
|
340
|
+
] = FormDeviceType.pc,
|
|
341
|
+
form_type: Annotated[
|
|
342
|
+
FormType,
|
|
343
|
+
typer.Option("--form-type", help="Form type"),
|
|
344
|
+
] = FormType.PC,
|
|
345
|
+
basic_info: Annotated[str, typer.Option("--basic-info", help="Basic info")] = "",
|
|
346
|
+
has_thumbnail: Annotated[
|
|
347
|
+
bool,
|
|
348
|
+
typer.Option("--has-thumbnail", help="Has thumbnail"),
|
|
349
|
+
] = False,
|
|
350
|
+
widget_annotation: Annotated[
|
|
351
|
+
str, typer.Option("--widget-annotation", help="Widget annotation")
|
|
352
|
+
] = "",
|
|
353
|
+
addin_hide_rule: Annotated[
|
|
354
|
+
str, typer.Option("--addin-hide-rule", help="Addin hide rule (JSON)")
|
|
355
|
+
] = "[]",
|
|
356
|
+
force_update: Annotated[
|
|
357
|
+
bool,
|
|
358
|
+
typer.Option("--force-update", help="Force overwrite"),
|
|
359
|
+
] = True,
|
|
360
|
+
dry_run: Annotated[
|
|
361
|
+
bool,
|
|
362
|
+
typer.Option("--dry-run", help="Preview without executing"),
|
|
363
|
+
] = False,
|
|
364
|
+
) -> None:
|
|
365
|
+
try:
|
|
366
|
+
content = ""
|
|
367
|
+
if v3_content_file:
|
|
368
|
+
v3_content_file = validate_path(v3_content_file)
|
|
369
|
+
content = v3_content_file.read_text(encoding="utf-8")
|
|
370
|
+
elif v3_content:
|
|
371
|
+
content = v3_content
|
|
372
|
+
else:
|
|
373
|
+
if is_tty():
|
|
374
|
+
content = typer.prompt("v3Content (form JSON)")
|
|
375
|
+
else:
|
|
376
|
+
raise DWFError("--v3-content or --v3-content-file is required")
|
|
377
|
+
|
|
378
|
+
import time
|
|
379
|
+
|
|
380
|
+
payload = _build_view_payload(
|
|
381
|
+
class_name=class_name,
|
|
382
|
+
view_name=view_name,
|
|
383
|
+
is_relation=is_relation,
|
|
384
|
+
v3_content=content,
|
|
385
|
+
display_name=display_name,
|
|
386
|
+
note=note,
|
|
387
|
+
device_type=device_type.value,
|
|
388
|
+
form_type=form_type.value,
|
|
389
|
+
basic_info=basic_info,
|
|
390
|
+
has_thumbnail=has_thumbnail,
|
|
391
|
+
widget_annotation=widget_annotation,
|
|
392
|
+
)
|
|
393
|
+
payload["oid"] = oid
|
|
394
|
+
payload["addinHideRule"] = addin_hide_rule
|
|
395
|
+
payload["lastModifyTime"] = int(time.time() * 1000)
|
|
396
|
+
|
|
397
|
+
if dry_run:
|
|
398
|
+
typer.echo(
|
|
399
|
+
json_mod.dumps(
|
|
400
|
+
{
|
|
401
|
+
"action": "update",
|
|
402
|
+
"resource": "view",
|
|
403
|
+
"target": oid,
|
|
404
|
+
"data": payload,
|
|
405
|
+
"reversible": True,
|
|
406
|
+
},
|
|
407
|
+
ensure_ascii=False,
|
|
408
|
+
indent=2,
|
|
409
|
+
)
|
|
410
|
+
)
|
|
411
|
+
raise typer.Exit(code=DRY_RUN_EXIT_CODE)
|
|
412
|
+
|
|
413
|
+
client = get_client(ctx)
|
|
414
|
+
result = formmodel_api.update_view(
|
|
415
|
+
client,
|
|
416
|
+
payload,
|
|
417
|
+
force_update=force_update,
|
|
418
|
+
)
|
|
419
|
+
typer.echo(f"Updated view: {oid}", err=True)
|
|
420
|
+
if isinstance(result, dict):
|
|
421
|
+
typer.echo(json_mod.dumps(result, ensure_ascii=False, indent=2))
|
|
422
|
+
except DWFError as exc:
|
|
423
|
+
handle_error(exc)
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
@_view_sub.command(name="copy")
|
|
427
|
+
def copy_view(
|
|
428
|
+
ctx: typer.Context,
|
|
429
|
+
oid: Annotated[str, typer.Argument(help="View OID to copy")],
|
|
430
|
+
new_class_name: Annotated[
|
|
431
|
+
str,
|
|
432
|
+
typer.Option("--new-class-name", help="Class name for the copied view"),
|
|
433
|
+
] = "",
|
|
434
|
+
new_view_name: Annotated[
|
|
435
|
+
str,
|
|
436
|
+
typer.Option("--new-view-name", "-n", help="View name for the copy"),
|
|
437
|
+
] = "",
|
|
438
|
+
new_device_type: Annotated[
|
|
439
|
+
FormDeviceType,
|
|
440
|
+
typer.Option("--new-device-type", help="Device type for the copy"),
|
|
441
|
+
] = FormDeviceType.pc,
|
|
442
|
+
new_display_name: Annotated[
|
|
443
|
+
str,
|
|
444
|
+
typer.Option("--new-display-name", help="Display name for the copy"),
|
|
445
|
+
] = "",
|
|
446
|
+
new_note: Annotated[
|
|
447
|
+
str,
|
|
448
|
+
typer.Option("--new-note", help="Note for the copy"),
|
|
449
|
+
] = "",
|
|
450
|
+
new_form_type: Annotated[
|
|
451
|
+
FormType,
|
|
452
|
+
typer.Option("--new-form-type", help="Form type: PC or Mobile"),
|
|
453
|
+
] = FormType.PC,
|
|
454
|
+
dry_run: Annotated[
|
|
455
|
+
bool,
|
|
456
|
+
typer.Option("--dry-run", help="Preview without executing"),
|
|
457
|
+
] = False,
|
|
458
|
+
) -> None:
|
|
459
|
+
try:
|
|
460
|
+
if dry_run:
|
|
461
|
+
typer.echo(
|
|
462
|
+
json_mod.dumps(
|
|
463
|
+
{
|
|
464
|
+
"action": "copy",
|
|
465
|
+
"resource": "view",
|
|
466
|
+
"target": oid,
|
|
467
|
+
"data": {
|
|
468
|
+
"newClassName": new_class_name,
|
|
469
|
+
"newViewName": new_view_name,
|
|
470
|
+
"newDeviceType": new_device_type.value,
|
|
471
|
+
"newDisplayName": new_display_name,
|
|
472
|
+
"newNote": new_note,
|
|
473
|
+
"newFormType": new_form_type.value,
|
|
474
|
+
},
|
|
475
|
+
"reversible": False,
|
|
476
|
+
},
|
|
477
|
+
ensure_ascii=False,
|
|
478
|
+
indent=2,
|
|
479
|
+
)
|
|
480
|
+
)
|
|
481
|
+
raise typer.Exit(code=DRY_RUN_EXIT_CODE)
|
|
482
|
+
|
|
483
|
+
client = get_client(ctx)
|
|
484
|
+
result = formmodel_api.copy_view(
|
|
485
|
+
client,
|
|
486
|
+
oid,
|
|
487
|
+
new_class_name=new_class_name,
|
|
488
|
+
new_view_name=new_view_name,
|
|
489
|
+
new_device_type=new_device_type.value,
|
|
490
|
+
new_display_name=new_display_name,
|
|
491
|
+
new_note=new_note,
|
|
492
|
+
new_form_type=new_form_type.value,
|
|
493
|
+
)
|
|
494
|
+
typer.echo(f"Copied view: {oid}", err=True)
|
|
495
|
+
if isinstance(result, dict):
|
|
496
|
+
typer.echo(json_mod.dumps(result, ensure_ascii=False, indent=2))
|
|
497
|
+
except DWFError as exc:
|
|
498
|
+
handle_error(exc)
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
@_view_sub.command(name="delete")
|
|
502
|
+
def delete_view(
|
|
503
|
+
ctx: typer.Context,
|
|
504
|
+
oid: Annotated[str, typer.Argument(help="View OID to delete")],
|
|
505
|
+
dry_run: Annotated[
|
|
506
|
+
bool,
|
|
507
|
+
typer.Option("--dry-run", help="Preview without executing"),
|
|
508
|
+
] = False,
|
|
509
|
+
yes: Annotated[
|
|
510
|
+
bool,
|
|
511
|
+
typer.Option("--force", help="Skip confirmation prompt"),
|
|
512
|
+
] = False,
|
|
513
|
+
) -> None:
|
|
514
|
+
try:
|
|
515
|
+
if dry_run:
|
|
516
|
+
typer.echo(
|
|
517
|
+
json_mod.dumps(
|
|
518
|
+
{
|
|
519
|
+
"action": "delete",
|
|
520
|
+
"resource": "view",
|
|
521
|
+
"target": oid,
|
|
522
|
+
"reversible": False,
|
|
523
|
+
},
|
|
524
|
+
ensure_ascii=False,
|
|
525
|
+
indent=2,
|
|
526
|
+
)
|
|
527
|
+
)
|
|
528
|
+
raise typer.Exit(code=DRY_RUN_EXIT_CODE)
|
|
529
|
+
|
|
530
|
+
if is_tty() and not yes:
|
|
531
|
+
confirm = typer.confirm(f"Delete view {oid}?", default=False)
|
|
532
|
+
if not confirm:
|
|
533
|
+
typer.echo("Cancelled.", err=True)
|
|
534
|
+
raise typer.Exit(code=0)
|
|
535
|
+
|
|
536
|
+
client = get_client(ctx)
|
|
537
|
+
formmodel_api.delete_view(client, oid)
|
|
538
|
+
typer.echo(f"Deleted view: {oid}", err=True)
|
|
539
|
+
except DWFError as exc:
|
|
540
|
+
handle_error(exc)
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
_component_sub = typer.Typer(
|
|
544
|
+
help="Component management.", context_settings=_HELP_CONTEXT_SETTINGS
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
app.add_typer(_component_sub, name="component")
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
@_component_sub.command(name="create-group")
|
|
551
|
+
def create_component_group(
|
|
552
|
+
ctx: typer.Context,
|
|
553
|
+
name: Annotated[str, typer.Option("--name", "-n", help="Group name")],
|
|
554
|
+
display_name: Annotated[
|
|
555
|
+
str,
|
|
556
|
+
typer.Option("--display-name", "-d", help="Display name"),
|
|
557
|
+
] = "",
|
|
558
|
+
dry_run: Annotated[
|
|
559
|
+
bool,
|
|
560
|
+
typer.Option("--dry-run", help="Preview without executing"),
|
|
561
|
+
] = False,
|
|
562
|
+
) -> None:
|
|
563
|
+
try:
|
|
564
|
+
if dry_run:
|
|
565
|
+
typer.echo(
|
|
566
|
+
json_mod.dumps(
|
|
567
|
+
{
|
|
568
|
+
"action": "create",
|
|
569
|
+
"resource": "component-group",
|
|
570
|
+
"data": {
|
|
571
|
+
"name": name,
|
|
572
|
+
"displayName": display_name,
|
|
573
|
+
},
|
|
574
|
+
"reversible": False,
|
|
575
|
+
},
|
|
576
|
+
ensure_ascii=False,
|
|
577
|
+
indent=2,
|
|
578
|
+
)
|
|
579
|
+
)
|
|
580
|
+
raise typer.Exit(code=DRY_RUN_EXIT_CODE)
|
|
581
|
+
|
|
582
|
+
client = get_client(ctx)
|
|
583
|
+
result = formmodel_api.create_component_group(client, name, display_name)
|
|
584
|
+
typer.echo(f"Created component group: {name}", err=True)
|
|
585
|
+
if isinstance(result, dict):
|
|
586
|
+
typer.echo(json_mod.dumps(result, ensure_ascii=False, indent=2))
|
|
587
|
+
except DWFError as exc:
|
|
588
|
+
handle_error(exc)
|
|
589
|
+
|
|
590
|
+
|
|
591
|
+
@_component_sub.command(name="tree")
|
|
592
|
+
def get_component_tree(
|
|
593
|
+
ctx: typer.Context,
|
|
594
|
+
need_v3_content: Annotated[
|
|
595
|
+
bool,
|
|
596
|
+
typer.Option(
|
|
597
|
+
"--need-v3-content",
|
|
598
|
+
help="Return v3Content and basicInfo",
|
|
599
|
+
),
|
|
600
|
+
] = False,
|
|
601
|
+
form_type: Annotated[
|
|
602
|
+
str,
|
|
603
|
+
typer.Option("--form-type", help="Form type filter: PC or Mobile"),
|
|
604
|
+
] = "",
|
|
605
|
+
fmt: Annotated[
|
|
606
|
+
OutputFormat,
|
|
607
|
+
typer.Option("--format", "-f", help="Output format: table/json"),
|
|
608
|
+
] = OutputFormat.json,
|
|
609
|
+
) -> None:
|
|
610
|
+
try:
|
|
611
|
+
client = get_client(ctx)
|
|
612
|
+
result = formmodel_api.get_component_tree(
|
|
613
|
+
client,
|
|
614
|
+
need_v3_content=need_v3_content,
|
|
615
|
+
form_type=form_type,
|
|
616
|
+
)
|
|
617
|
+
fmt = resolve_format(fmt)
|
|
618
|
+
typer.echo(json_mod.dumps(result, ensure_ascii=False, indent=2))
|
|
619
|
+
except DWFError as exc:
|
|
620
|
+
handle_error(exc)
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
@_component_sub.command(name="create")
|
|
624
|
+
def create_component(
|
|
625
|
+
ctx: typer.Context,
|
|
626
|
+
view_name: Annotated[
|
|
627
|
+
str, typer.Option("--view-name", "-n", help="Component name (English)")
|
|
628
|
+
],
|
|
629
|
+
display_name: Annotated[
|
|
630
|
+
str, typer.Option("--display-name", "-d", help="Component display name")
|
|
631
|
+
],
|
|
632
|
+
form_type: Annotated[
|
|
633
|
+
FormType,
|
|
634
|
+
typer.Option("--form-type", help="Form type: PC or Mobile"),
|
|
635
|
+
],
|
|
636
|
+
icon: Annotated[str, typer.Option("--icon", help="Icon")],
|
|
637
|
+
owner: Annotated[str, typer.Option("--owner", help="Owner component group OID")],
|
|
638
|
+
basic_info: Annotated[str, typer.Option("--basic-info", help="Basic info")] = "",
|
|
639
|
+
note: Annotated[str, typer.Option("--note", help="Note")] = "",
|
|
640
|
+
v3_content: Annotated[
|
|
641
|
+
str | None,
|
|
642
|
+
typer.Option("--v3-content", help="Form JSON content (string)"),
|
|
643
|
+
] = None,
|
|
644
|
+
v3_content_file: Annotated[
|
|
645
|
+
Path | None,
|
|
646
|
+
typer.Option("--v3-content-file", help="Path to form JSON content file"),
|
|
647
|
+
] = None,
|
|
648
|
+
widget_annotation: Annotated[
|
|
649
|
+
str, typer.Option("--widget-annotation", help="Widget annotation (JSON)")
|
|
650
|
+
] = "{}",
|
|
651
|
+
dry_run: Annotated[
|
|
652
|
+
bool,
|
|
653
|
+
typer.Option("--dry-run", help="Preview without executing"),
|
|
654
|
+
] = False,
|
|
655
|
+
) -> None:
|
|
656
|
+
try:
|
|
657
|
+
content = ""
|
|
658
|
+
if v3_content_file:
|
|
659
|
+
v3_content_file = validate_path(v3_content_file)
|
|
660
|
+
content = v3_content_file.read_text(encoding="utf-8")
|
|
661
|
+
elif v3_content:
|
|
662
|
+
content = v3_content
|
|
663
|
+
else:
|
|
664
|
+
if is_tty():
|
|
665
|
+
content = typer.prompt("v3Content (form JSON)")
|
|
666
|
+
else:
|
|
667
|
+
raise DWFError("--v3-content or --v3-content-file is required")
|
|
668
|
+
|
|
669
|
+
payload = {
|
|
670
|
+
"basicInfo": basic_info,
|
|
671
|
+
"displayName": display_name,
|
|
672
|
+
"formType": form_type.value,
|
|
673
|
+
"icon": icon,
|
|
674
|
+
"note": note,
|
|
675
|
+
"owner": owner,
|
|
676
|
+
"v3Content": content,
|
|
677
|
+
"viewName": view_name,
|
|
678
|
+
"widgetAnnotation": widget_annotation,
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if dry_run:
|
|
682
|
+
typer.echo(
|
|
683
|
+
json_mod.dumps(
|
|
684
|
+
{
|
|
685
|
+
"action": "create",
|
|
686
|
+
"resource": "component",
|
|
687
|
+
"data": [payload],
|
|
688
|
+
"reversible": False,
|
|
689
|
+
},
|
|
690
|
+
ensure_ascii=False,
|
|
691
|
+
indent=2,
|
|
692
|
+
)
|
|
693
|
+
)
|
|
694
|
+
raise typer.Exit(code=DRY_RUN_EXIT_CODE)
|
|
695
|
+
|
|
696
|
+
client = get_client(ctx)
|
|
697
|
+
result = formmodel_api.create_component(client, [payload])
|
|
698
|
+
typer.echo(f"Created component: {view_name}", err=True)
|
|
699
|
+
if isinstance(result, dict):
|
|
700
|
+
typer.echo(json_mod.dumps(result, ensure_ascii=False, indent=2))
|
|
701
|
+
except DWFError as exc:
|
|
702
|
+
handle_error(exc)
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
@_component_sub.command(name="update-group-rel")
|
|
706
|
+
def update_comp2compgroup(
|
|
707
|
+
ctx: typer.Context,
|
|
708
|
+
oid: Annotated[str, typer.Option("--oid", help="Relation OID of the component")],
|
|
709
|
+
component_oid: Annotated[
|
|
710
|
+
str, typer.Option("--component-oid", help="Component OID")
|
|
711
|
+
],
|
|
712
|
+
component_group_oid: Annotated[
|
|
713
|
+
str,
|
|
714
|
+
typer.Option("--component-group-oid", help="Target component group OID"),
|
|
715
|
+
],
|
|
716
|
+
component_display_name: Annotated[
|
|
717
|
+
str,
|
|
718
|
+
typer.Option("--display-name", "-d", help="Component display name"),
|
|
719
|
+
] = "",
|
|
720
|
+
component_icon: Annotated[
|
|
721
|
+
str,
|
|
722
|
+
typer.Option("--icon", help="Component icon"),
|
|
723
|
+
] = "",
|
|
724
|
+
component_note: Annotated[
|
|
725
|
+
str,
|
|
726
|
+
typer.Option("--note", help="Component note"),
|
|
727
|
+
] = "",
|
|
728
|
+
dry_run: Annotated[
|
|
729
|
+
bool,
|
|
730
|
+
typer.Option("--dry-run", help="Preview without executing"),
|
|
731
|
+
] = False,
|
|
732
|
+
) -> None:
|
|
733
|
+
try:
|
|
734
|
+
if dry_run:
|
|
735
|
+
typer.echo(
|
|
736
|
+
json_mod.dumps(
|
|
737
|
+
{
|
|
738
|
+
"action": "update",
|
|
739
|
+
"resource": "comp2compgroup",
|
|
740
|
+
"data": {
|
|
741
|
+
"componentDisplayName": component_display_name,
|
|
742
|
+
"componentGroupOid": component_group_oid,
|
|
743
|
+
"componentIcon": component_icon,
|
|
744
|
+
"componentNote": component_note,
|
|
745
|
+
"componentOid": component_oid,
|
|
746
|
+
"oid": oid,
|
|
747
|
+
},
|
|
748
|
+
"reversible": True,
|
|
749
|
+
},
|
|
750
|
+
ensure_ascii=False,
|
|
751
|
+
indent=2,
|
|
752
|
+
)
|
|
753
|
+
)
|
|
754
|
+
raise typer.Exit(code=DRY_RUN_EXIT_CODE)
|
|
755
|
+
|
|
756
|
+
client = get_client(ctx)
|
|
757
|
+
result = formmodel_api.update_comp2compgroup(
|
|
758
|
+
client,
|
|
759
|
+
component_display_name=component_display_name,
|
|
760
|
+
component_group_oid=component_group_oid,
|
|
761
|
+
component_icon=component_icon,
|
|
762
|
+
component_note=component_note,
|
|
763
|
+
component_oid=component_oid,
|
|
764
|
+
oid=oid,
|
|
765
|
+
)
|
|
766
|
+
typer.echo(f"Updated comp2compgroup: {oid}", err=True)
|
|
767
|
+
if isinstance(result, dict):
|
|
768
|
+
typer.echo(json_mod.dumps(result, ensure_ascii=False, indent=2))
|
|
769
|
+
except DWFError as exc:
|
|
770
|
+
handle_error(exc)
|
|
771
|
+
|
|
772
|
+
|
|
773
|
+
@_component_sub.command(name="delete")
|
|
774
|
+
def delete_component(
|
|
775
|
+
ctx: typer.Context,
|
|
776
|
+
oid: Annotated[str, typer.Argument(help="Component OID to delete")],
|
|
777
|
+
dry_run: Annotated[
|
|
778
|
+
bool,
|
|
779
|
+
typer.Option("--dry-run", help="Preview without executing"),
|
|
780
|
+
] = False,
|
|
781
|
+
yes: Annotated[
|
|
782
|
+
bool,
|
|
783
|
+
typer.Option("--force", help="Skip confirmation prompt"),
|
|
784
|
+
] = False,
|
|
785
|
+
) -> None:
|
|
786
|
+
try:
|
|
787
|
+
if dry_run:
|
|
788
|
+
typer.echo(
|
|
789
|
+
json_mod.dumps(
|
|
790
|
+
{
|
|
791
|
+
"action": "delete",
|
|
792
|
+
"resource": "component",
|
|
793
|
+
"target": oid,
|
|
794
|
+
"reversible": False,
|
|
795
|
+
},
|
|
796
|
+
ensure_ascii=False,
|
|
797
|
+
indent=2,
|
|
798
|
+
)
|
|
799
|
+
)
|
|
800
|
+
raise typer.Exit(code=DRY_RUN_EXIT_CODE)
|
|
801
|
+
|
|
802
|
+
if is_tty() and not yes:
|
|
803
|
+
confirm = typer.confirm(f"Delete component {oid}?", default=False)
|
|
804
|
+
if not confirm:
|
|
805
|
+
typer.echo("Cancelled.", err=True)
|
|
806
|
+
raise typer.Exit(code=0)
|
|
807
|
+
|
|
808
|
+
client = get_client(ctx)
|
|
809
|
+
formmodel_api.delete_component(client, oid)
|
|
810
|
+
typer.echo(f"Deleted component: {oid}", err=True)
|
|
811
|
+
except DWFError as exc:
|
|
812
|
+
handle_error(exc)
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
@_component_sub.command(name="delete-group")
|
|
816
|
+
def delete_component_group(
|
|
817
|
+
ctx: typer.Context,
|
|
818
|
+
oid: Annotated[str, typer.Argument(help="Component group OID to delete")],
|
|
819
|
+
dry_run: Annotated[
|
|
820
|
+
bool,
|
|
821
|
+
typer.Option("--dry-run", help="Preview without executing"),
|
|
822
|
+
] = False,
|
|
823
|
+
yes: Annotated[
|
|
824
|
+
bool,
|
|
825
|
+
typer.Option("--force", help="Skip confirmation prompt"),
|
|
826
|
+
] = False,
|
|
827
|
+
) -> None:
|
|
828
|
+
try:
|
|
829
|
+
if dry_run:
|
|
830
|
+
typer.echo(
|
|
831
|
+
json_mod.dumps(
|
|
832
|
+
{
|
|
833
|
+
"action": "delete",
|
|
834
|
+
"resource": "component-group",
|
|
835
|
+
"target": oid,
|
|
836
|
+
"reversible": False,
|
|
837
|
+
},
|
|
838
|
+
ensure_ascii=False,
|
|
839
|
+
indent=2,
|
|
840
|
+
)
|
|
841
|
+
)
|
|
842
|
+
raise typer.Exit(code=DRY_RUN_EXIT_CODE)
|
|
843
|
+
|
|
844
|
+
if is_tty() and not yes:
|
|
845
|
+
confirm = typer.confirm(f"Delete component group {oid}?", default=False)
|
|
846
|
+
if not confirm:
|
|
847
|
+
typer.echo("Cancelled.", err=True)
|
|
848
|
+
raise typer.Exit(code=0)
|
|
849
|
+
|
|
850
|
+
client = get_client(ctx)
|
|
851
|
+
formmodel_api.delete_component_group(client, oid)
|
|
852
|
+
typer.echo(f"Deleted component group: {oid}", err=True)
|
|
853
|
+
except DWFError as exc:
|
|
854
|
+
handle_error(exc)
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
@_view_sub.command(name="get-by-device")
|
|
858
|
+
def get_view_by_device(
|
|
859
|
+
ctx: typer.Context,
|
|
860
|
+
class_name: Annotated[str, typer.Option("--class-name", "-c", help="Class name")],
|
|
861
|
+
view_name: Annotated[str, typer.Option("--view-name", "-n", help="View name")],
|
|
862
|
+
device_type: Annotated[
|
|
863
|
+
FormDeviceType,
|
|
864
|
+
typer.Option("--device-type", help="Device type"),
|
|
865
|
+
],
|
|
866
|
+
form_type: Annotated[
|
|
867
|
+
FormType,
|
|
868
|
+
typer.Option("--form-type", help="Form type: PC or Mobile"),
|
|
869
|
+
],
|
|
870
|
+
fmt: Annotated[
|
|
871
|
+
OutputFormat,
|
|
872
|
+
typer.Option("--format", "-f", help="Output format: table/json"),
|
|
873
|
+
] = OutputFormat.json,
|
|
874
|
+
) -> None:
|
|
875
|
+
try:
|
|
876
|
+
client = get_client(ctx)
|
|
877
|
+
result = formmodel_api.get_view_by_device_type(
|
|
878
|
+
client,
|
|
879
|
+
class_name=class_name,
|
|
880
|
+
view_name=view_name,
|
|
881
|
+
device_type=device_type.value,
|
|
882
|
+
form_type=form_type.value,
|
|
883
|
+
)
|
|
884
|
+
resolve_format(fmt)
|
|
885
|
+
typer.echo(json_mod.dumps(result, ensure_ascii=False, indent=2))
|
|
886
|
+
except DWFError as exc:
|
|
887
|
+
handle_error(exc)
|
|
888
|
+
|
|
889
|
+
|
|
890
|
+
_library_sub = typer.Typer(
|
|
891
|
+
help="Library management.", context_settings=_HELP_CONTEXT_SETTINGS
|
|
892
|
+
)
|
|
893
|
+
|
|
894
|
+
app.add_typer(_library_sub, name="library")
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
@_library_sub.command(name="files")
|
|
898
|
+
def list_library_files(
|
|
899
|
+
ctx: typer.Context,
|
|
900
|
+
library_id: Annotated[
|
|
901
|
+
str,
|
|
902
|
+
typer.Option("--library-id", "-l", help="Library ID"),
|
|
903
|
+
] = "picture_management",
|
|
904
|
+
fmt: Annotated[
|
|
905
|
+
OutputFormat,
|
|
906
|
+
typer.Option("--format", "-f", help="Output format: table/json"),
|
|
907
|
+
] = OutputFormat.json,
|
|
908
|
+
) -> None:
|
|
909
|
+
try:
|
|
910
|
+
client = get_client(ctx)
|
|
911
|
+
result = formmodel_api.list_library_files(client, library_id)
|
|
912
|
+
resolve_format(fmt)
|
|
913
|
+
typer.echo(json_mod.dumps(result, ensure_ascii=False, indent=2))
|
|
914
|
+
except DWFError as exc:
|
|
915
|
+
handle_error(exc)
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
@_library_sub.command(name="upload")
|
|
919
|
+
def upload_file(
|
|
920
|
+
ctx: typer.Context,
|
|
921
|
+
file_path: Annotated[
|
|
922
|
+
Path,
|
|
923
|
+
typer.Option("--file-path", "-p", help="Path to the file to upload"),
|
|
924
|
+
],
|
|
925
|
+
library_id: Annotated[
|
|
926
|
+
str,
|
|
927
|
+
typer.Option("--library-id", "-l", help="Library ID"),
|
|
928
|
+
] = "",
|
|
929
|
+
dry_run: Annotated[
|
|
930
|
+
bool,
|
|
931
|
+
typer.Option("--dry-run", help="Preview without executing"),
|
|
932
|
+
] = False,
|
|
933
|
+
) -> None:
|
|
934
|
+
try:
|
|
935
|
+
file_path = validate_path(file_path)
|
|
936
|
+
|
|
937
|
+
if dry_run:
|
|
938
|
+
typer.echo(
|
|
939
|
+
json_mod.dumps(
|
|
940
|
+
{
|
|
941
|
+
"action": "upload",
|
|
942
|
+
"resource": "file",
|
|
943
|
+
"file": str(file_path),
|
|
944
|
+
"library_id": library_id or None,
|
|
945
|
+
"reversible": False,
|
|
946
|
+
},
|
|
947
|
+
ensure_ascii=False,
|
|
948
|
+
indent=2,
|
|
949
|
+
)
|
|
950
|
+
)
|
|
951
|
+
raise typer.Exit(code=DRY_RUN_EXIT_CODE)
|
|
952
|
+
|
|
953
|
+
file_content = file_path.read_bytes()
|
|
954
|
+
client = get_client(ctx)
|
|
955
|
+
result = formmodel_api.upload_file(
|
|
956
|
+
client,
|
|
957
|
+
file_content,
|
|
958
|
+
file_path.name,
|
|
959
|
+
library_id=library_id,
|
|
960
|
+
)
|
|
961
|
+
typer.echo(f"Uploaded file: {file_path.name}", err=True)
|
|
962
|
+
if isinstance(result, dict):
|
|
963
|
+
typer.echo(json_mod.dumps(result, ensure_ascii=False, indent=2))
|
|
964
|
+
except DWFError as exc:
|
|
965
|
+
handle_error(exc)
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
@_library_sub.command(name="delete")
|
|
969
|
+
def delete_file(
|
|
970
|
+
ctx: typer.Context,
|
|
971
|
+
file_oid: Annotated[str, typer.Argument(help="File OID to delete")],
|
|
972
|
+
dry_run: Annotated[
|
|
973
|
+
bool,
|
|
974
|
+
typer.Option("--dry-run", help="Preview without executing"),
|
|
975
|
+
] = False,
|
|
976
|
+
yes: Annotated[
|
|
977
|
+
bool,
|
|
978
|
+
typer.Option("--force", help="Skip confirmation prompt"),
|
|
979
|
+
] = False,
|
|
980
|
+
) -> None:
|
|
981
|
+
try:
|
|
982
|
+
if dry_run:
|
|
983
|
+
typer.echo(
|
|
984
|
+
json_mod.dumps(
|
|
985
|
+
{
|
|
986
|
+
"action": "delete",
|
|
987
|
+
"resource": "file",
|
|
988
|
+
"target": file_oid,
|
|
989
|
+
"reversible": False,
|
|
990
|
+
},
|
|
991
|
+
ensure_ascii=False,
|
|
992
|
+
indent=2,
|
|
993
|
+
)
|
|
994
|
+
)
|
|
995
|
+
raise typer.Exit(code=DRY_RUN_EXIT_CODE)
|
|
996
|
+
|
|
997
|
+
if is_tty() and not yes:
|
|
998
|
+
confirm = typer.confirm(f"Delete file {file_oid}?", default=False)
|
|
999
|
+
if not confirm:
|
|
1000
|
+
typer.echo("Cancelled.", err=True)
|
|
1001
|
+
raise typer.Exit(code=0)
|
|
1002
|
+
|
|
1003
|
+
client = get_client(ctx)
|
|
1004
|
+
formmodel_api.delete_file(client, file_oid)
|
|
1005
|
+
typer.echo(f"Deleted file: {file_oid}", err=True)
|
|
1006
|
+
except DWFError as exc:
|
|
1007
|
+
handle_error(exc)
|