tabsdata-cli 2.0.0__cp312-abi3-win_amd64.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.
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/__init__.py +5 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/_registry.py +78 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/assets/manifest/BANNER +5 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/assets/manifest/LICENSE +3 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/assets/manifest/README-PyPi.md +8 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/assets/manifest/RELEASE-NOTES +1 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/assets/manifest/THIRD-PARTY +3 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/assets/manifest/VERSION +1 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/cli_utils.py +993 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/shell.py +390 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/__init__.py +3 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/auth_group.py +197 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/cli.py +508 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/collection_group.py +458 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/config_utils.py +93 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/connection_group.py +159 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/examples_group.py +206 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/execution_utils.py +429 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/fn_group.py +1109 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/fnrun_group.py +285 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/group_group.py +184 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/metrics_group.py +323 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/permission_group.py +259 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/plan_group.py +324 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/project_group.py +170 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/role_group.py +630 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/settings_group.py +344 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/table_group.py +600 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/trigger_group.py +338 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/trx_group.py +329 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/user_group.py +200 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/worker_group.py +185 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/template.py +781 -0
- tabsdata_cli-2.0.0.data/purelib/tabsdatak/py.typed +0 -0
- tabsdata_cli-2.0.0.dist-info/METADATA +39 -0
- tabsdata_cli-2.0.0.dist-info/RECORD +39 -0
- tabsdata_cli-2.0.0.dist-info/WHEEL +5 -0
- tabsdata_cli-2.0.0.dist-info/licenses/src/main/python/tabsdatak/cli/assets/manifest/LICENSE +3 -0
- tabsdata_cli-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2026 Tabsdata Inc.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING, List
|
|
8
|
+
|
|
9
|
+
import rich_click as click
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
|
|
12
|
+
from tabsdatak.cli._registry import cli_command
|
|
13
|
+
from tabsdatak.cli.cli_utils import (
|
|
14
|
+
beautify_time,
|
|
15
|
+
collection_label,
|
|
16
|
+
echo_more_results,
|
|
17
|
+
format_option,
|
|
18
|
+
hint_common_solutions,
|
|
19
|
+
logical_prompt,
|
|
20
|
+
make_console,
|
|
21
|
+
max_result_option,
|
|
22
|
+
output_format,
|
|
23
|
+
render,
|
|
24
|
+
take,
|
|
25
|
+
verify_login_or_prompt,
|
|
26
|
+
)
|
|
27
|
+
from tabsdatak.cli.td.execution_utils import (
|
|
28
|
+
echo_plan_status,
|
|
29
|
+
echo_transaction_status,
|
|
30
|
+
emit_worker_logs,
|
|
31
|
+
function_runs_table,
|
|
32
|
+
obtain_list_fn_run_filters,
|
|
33
|
+
pick_worker,
|
|
34
|
+
span_projects,
|
|
35
|
+
workers_table,
|
|
36
|
+
)
|
|
37
|
+
from tabsdatak.sdk.status_utils.function_run import (
|
|
38
|
+
FUNCTION_RUN_VALID_USER_PROVIDED_STATUSES,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if TYPE_CHECKING:
|
|
42
|
+
from tabsdatak.sdk.tabsdata_server import TabsdataServer
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@cli_command
|
|
46
|
+
@click.group()
|
|
47
|
+
def fnrun():
|
|
48
|
+
"""Function run management commands"""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@fnrun.command(name="list")
|
|
52
|
+
@click.option(
|
|
53
|
+
"--proj", type=str, help="Name of the project to filter function runs by."
|
|
54
|
+
)
|
|
55
|
+
@click.option(
|
|
56
|
+
"--status",
|
|
57
|
+
type=str,
|
|
58
|
+
multiple=True,
|
|
59
|
+
help=(
|
|
60
|
+
"Filter function runs by status. The possible statuses are "
|
|
61
|
+
f"'{FUNCTION_RUN_VALID_USER_PROVIDED_STATUSES}'. This field is "
|
|
62
|
+
"case-insensitive. "
|
|
63
|
+
),
|
|
64
|
+
)
|
|
65
|
+
@click.option(
|
|
66
|
+
"--coll",
|
|
67
|
+
type=str,
|
|
68
|
+
help="Collection of the function to which the function runs belong.",
|
|
69
|
+
)
|
|
70
|
+
@click.option(
|
|
71
|
+
"--fn",
|
|
72
|
+
type=str,
|
|
73
|
+
help="Name of the function to which the function runs belong.",
|
|
74
|
+
)
|
|
75
|
+
@click.option("--plan", type=str, help="ID of the plan to filter function runs by.")
|
|
76
|
+
@click.option(
|
|
77
|
+
"--plan-name",
|
|
78
|
+
help=(
|
|
79
|
+
"A plan name wildcard to match. "
|
|
80
|
+
"For example, 'my_plan*' will match all plans "
|
|
81
|
+
"with names starting with 'my_plan'."
|
|
82
|
+
),
|
|
83
|
+
)
|
|
84
|
+
@click.option(
|
|
85
|
+
"--trx", type=str, help="ID of the transaction to filter function runs by."
|
|
86
|
+
)
|
|
87
|
+
@format_option
|
|
88
|
+
@max_result_option
|
|
89
|
+
@click.pass_context
|
|
90
|
+
def list_runs(
|
|
91
|
+
ctx: click.Context,
|
|
92
|
+
proj: str,
|
|
93
|
+
status: List[str],
|
|
94
|
+
coll: str,
|
|
95
|
+
fn: str,
|
|
96
|
+
plan: str,
|
|
97
|
+
plan_name: str,
|
|
98
|
+
trx: str,
|
|
99
|
+
):
|
|
100
|
+
"""List function runs across every project you can view."""
|
|
101
|
+
verify_login_or_prompt(ctx)
|
|
102
|
+
try:
|
|
103
|
+
server: TabsdataServer = ctx.obj["tabsdataserver"]
|
|
104
|
+
span_projects(server)
|
|
105
|
+
request_filter = obtain_list_fn_run_filters(
|
|
106
|
+
status=status,
|
|
107
|
+
exe_name=plan_name,
|
|
108
|
+
execution_id=plan,
|
|
109
|
+
fn=fn,
|
|
110
|
+
collection=coll,
|
|
111
|
+
trx=trx,
|
|
112
|
+
project=proj,
|
|
113
|
+
)
|
|
114
|
+
if output_format(ctx) == "table":
|
|
115
|
+
if plan:
|
|
116
|
+
echo_plan_status(server, plan)
|
|
117
|
+
if trx:
|
|
118
|
+
echo_transaction_status(server, trx)
|
|
119
|
+
list_of_function_runs, truncated = take(
|
|
120
|
+
ctx, server.list_function_runs_generator(filter=request_filter)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
if output_format(ctx) != "table":
|
|
124
|
+
render(
|
|
125
|
+
ctx,
|
|
126
|
+
data=[r._data for r in list_of_function_runs],
|
|
127
|
+
table=lambda: None,
|
|
128
|
+
)
|
|
129
|
+
echo_more_results(ctx, truncated)
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
click.echo()
|
|
133
|
+
console = make_console()
|
|
134
|
+
console.print(function_runs_table(list_of_function_runs))
|
|
135
|
+
click.echo(f"Number of function runs: {len(list_of_function_runs)}")
|
|
136
|
+
echo_more_results(ctx, truncated)
|
|
137
|
+
click.echo()
|
|
138
|
+
except Exception as e:
|
|
139
|
+
hint_common_solutions(ctx, e)
|
|
140
|
+
raise click.ClickException(f"Failed to list function runs: {e}")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@fnrun.command()
|
|
144
|
+
@click.option("--fnrun", required=True, help="ID of the function run to display.")
|
|
145
|
+
@format_option
|
|
146
|
+
@click.pass_context
|
|
147
|
+
def get(ctx: click.Context, fnrun: str):
|
|
148
|
+
"""
|
|
149
|
+
Display a function run: its details plus its workers.
|
|
150
|
+
"""
|
|
151
|
+
verify_login_or_prompt(ctx)
|
|
152
|
+
try:
|
|
153
|
+
server: TabsdataServer = ctx.obj["tabsdataserver"]
|
|
154
|
+
span_projects(server)
|
|
155
|
+
function_run = server.get_function_run(fnrun)
|
|
156
|
+
workers = server.list_workers(filter=f"logical_function_run_id:eq:{fnrun}")
|
|
157
|
+
|
|
158
|
+
if output_format(ctx) != "table":
|
|
159
|
+
render(
|
|
160
|
+
ctx,
|
|
161
|
+
data={
|
|
162
|
+
"function_run": function_run._data,
|
|
163
|
+
"workers": [w._data for w in workers],
|
|
164
|
+
},
|
|
165
|
+
table=lambda: None,
|
|
166
|
+
)
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
table = Table(title="Function run")
|
|
170
|
+
table.add_column("Project")
|
|
171
|
+
table.add_column("Collection")
|
|
172
|
+
table.add_column("Function")
|
|
173
|
+
table.add_column("Plan name")
|
|
174
|
+
table.add_column("Plan ID", no_wrap=True)
|
|
175
|
+
table.add_column("Transaction ID", no_wrap=True)
|
|
176
|
+
table.add_column("Started on")
|
|
177
|
+
table.add_column("Ended on")
|
|
178
|
+
table.add_column("Status", no_wrap=True)
|
|
179
|
+
table.add_row(
|
|
180
|
+
function_run.project or "-",
|
|
181
|
+
collection_label(function_run),
|
|
182
|
+
function_run.function.name,
|
|
183
|
+
function_run.execution.name,
|
|
184
|
+
function_run.execution.id,
|
|
185
|
+
function_run.transaction.id,
|
|
186
|
+
beautify_time(function_run.started_on_str),
|
|
187
|
+
beautify_time(function_run.ended_on_str),
|
|
188
|
+
function_run.status,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
click.echo()
|
|
192
|
+
console = make_console()
|
|
193
|
+
console.print(table)
|
|
194
|
+
console.print(workers_table(workers))
|
|
195
|
+
click.echo()
|
|
196
|
+
|
|
197
|
+
except Exception as e:
|
|
198
|
+
hint_common_solutions(ctx, e)
|
|
199
|
+
raise click.ClickException(f"Failed to display function run: {e}")
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@fnrun.command()
|
|
203
|
+
@click.option(
|
|
204
|
+
"--fnrun", required=True, help="ID of the function run whose logs to show."
|
|
205
|
+
)
|
|
206
|
+
@click.option(
|
|
207
|
+
"--file",
|
|
208
|
+
help=(
|
|
209
|
+
"Path of the file where the logs will be saved, or '-' to write them to "
|
|
210
|
+
"stdout. Any folders provided before the file must already exist."
|
|
211
|
+
),
|
|
212
|
+
)
|
|
213
|
+
@format_option
|
|
214
|
+
@click.pass_context
|
|
215
|
+
def logs(ctx: click.Context, fnrun: str, file: str):
|
|
216
|
+
"""
|
|
217
|
+
Show logs of one of the function run's workers; when several ran (retries),
|
|
218
|
+
pick one from the listing.
|
|
219
|
+
"""
|
|
220
|
+
verify_login_or_prompt(ctx)
|
|
221
|
+
try:
|
|
222
|
+
server: TabsdataServer = ctx.obj["tabsdataserver"]
|
|
223
|
+
span_projects(server)
|
|
224
|
+
target = pick_worker(
|
|
225
|
+
ctx,
|
|
226
|
+
server,
|
|
227
|
+
[f"logical_function_run_id:eq:{fnrun}"],
|
|
228
|
+
f"Function run '{fnrun}'",
|
|
229
|
+
)
|
|
230
|
+
emit_worker_logs(ctx, server, target, file)
|
|
231
|
+
except click.exceptions.Exit:
|
|
232
|
+
raise
|
|
233
|
+
except Exception as e:
|
|
234
|
+
hint_common_solutions(ctx, e)
|
|
235
|
+
raise click.ClickException(f"Failed to show logs: {e}")
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@fnrun.command()
|
|
239
|
+
@click.option("--fnrun", help="ID of the function run to replay.")
|
|
240
|
+
@click.pass_context
|
|
241
|
+
def replay(ctx: click.Context, fnrun: str):
|
|
242
|
+
"""Re-execute a finished function run.
|
|
243
|
+
|
|
244
|
+
Mints a new concrete physical function run against the latest patch (or
|
|
245
|
+
head, if no patch exists). The pointer stays bound to the previous
|
|
246
|
+
attempt's outputs until the new attempt finishes successfully.
|
|
247
|
+
"""
|
|
248
|
+
verify_login_or_prompt(ctx)
|
|
249
|
+
fnrun = fnrun or logical_prompt(ctx, "ID of the function run to replay")
|
|
250
|
+
click.echo(f"Replaying function run '{fnrun}'")
|
|
251
|
+
click.echo("-" * 10)
|
|
252
|
+
try:
|
|
253
|
+
server: TabsdataServer = ctx.obj["tabsdataserver"]
|
|
254
|
+
result = server.replay_logical_function_run(fnrun)
|
|
255
|
+
click.echo("Replay scheduled")
|
|
256
|
+
click.echo(f"Run id: {result.get('logical_function_run_id')}")
|
|
257
|
+
click.echo(f"Previous FR (bound): {result.get('previous_function_run_id')}")
|
|
258
|
+
click.echo(f"New FR (running): {result.get('new_function_run_id')}")
|
|
259
|
+
click.echo(f"Function version id: {result.get('function_version_id')}")
|
|
260
|
+
except Exception as e:
|
|
261
|
+
hint_common_solutions(ctx, e)
|
|
262
|
+
raise click.ClickException(f"Failed to replay function run: {e}")
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
@fnrun.command()
|
|
266
|
+
@click.option("--fnrun", required=True, help="ID of the function run to recover.")
|
|
267
|
+
@click.pass_context
|
|
268
|
+
def recover(ctx: click.Context, fnrun: str):
|
|
269
|
+
"""
|
|
270
|
+
Recover the transaction a function run belongs to: the transaction's
|
|
271
|
+
failed or on-hold function runs are re-scheduled.
|
|
272
|
+
"""
|
|
273
|
+
verify_login_or_prompt(ctx)
|
|
274
|
+
try:
|
|
275
|
+
server: TabsdataServer = ctx.obj["tabsdataserver"]
|
|
276
|
+
span_projects(server)
|
|
277
|
+
function_run = server.get_function_run(fnrun)
|
|
278
|
+
trx = function_run.transaction.id
|
|
279
|
+
click.echo(f"Recovering transaction '{trx}' of function run '{fnrun}'")
|
|
280
|
+
click.echo("-" * 10)
|
|
281
|
+
server.recover_transaction(trx)
|
|
282
|
+
click.echo("Transaction recovered successfully")
|
|
283
|
+
except Exception as e:
|
|
284
|
+
hint_common_solutions(ctx, e)
|
|
285
|
+
raise click.ClickException(f"Failed to recover function run: {e}")
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2026 Tabsdata Inc.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
import rich_click as click
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
|
|
12
|
+
from tabsdatak.cli._registry import cli_command
|
|
13
|
+
from tabsdatak.cli.cli_utils import (
|
|
14
|
+
beautify_list,
|
|
15
|
+
echo_more_results,
|
|
16
|
+
format_option,
|
|
17
|
+
hint_common_solutions,
|
|
18
|
+
logical_prompt,
|
|
19
|
+
make_console,
|
|
20
|
+
max_result_option,
|
|
21
|
+
proj_option,
|
|
22
|
+
render,
|
|
23
|
+
take,
|
|
24
|
+
verify_login_or_prompt,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from tabsdatak.sdk.tabsdata_server import TabsdataServer
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@cli_command
|
|
32
|
+
@click.group()
|
|
33
|
+
def group():
|
|
34
|
+
"""Group management commands"""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@group.command()
|
|
38
|
+
@proj_option
|
|
39
|
+
@click.option("--name", "-n", help="Name of the group to create.")
|
|
40
|
+
@click.option("--description", help="Description of the group.")
|
|
41
|
+
@click.pass_context
|
|
42
|
+
def create(ctx: click.Context, name: str, description: str):
|
|
43
|
+
"""Create a new group"""
|
|
44
|
+
verify_login_or_prompt(ctx)
|
|
45
|
+
name = name or logical_prompt(ctx, "Name of the group to create")
|
|
46
|
+
description = description or ""
|
|
47
|
+
click.echo("Creating a new group")
|
|
48
|
+
click.echo("-" * 10)
|
|
49
|
+
try:
|
|
50
|
+
server: TabsdataServer = ctx.obj["tabsdataserver"]
|
|
51
|
+
server.create_group(name, description)
|
|
52
|
+
click.echo("Group created successfully")
|
|
53
|
+
except Exception as e:
|
|
54
|
+
hint_common_solutions(ctx, e)
|
|
55
|
+
raise click.ClickException(f"Failed to create group: {e}")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@group.command()
|
|
59
|
+
@proj_option
|
|
60
|
+
@click.option("--name", "-n", help="Name of the group to delete.")
|
|
61
|
+
@click.option(
|
|
62
|
+
"--confirm",
|
|
63
|
+
help="Write 'delete' to confirm deletion. Will be prompted for it if not provided.",
|
|
64
|
+
)
|
|
65
|
+
@click.pass_context
|
|
66
|
+
def delete(ctx: click.Context, name: str, confirm: str):
|
|
67
|
+
"""Delete a group by name. System groups cannot be deleted."""
|
|
68
|
+
verify_login_or_prompt(ctx)
|
|
69
|
+
name = name or logical_prompt(ctx, "Name of the group to delete")
|
|
70
|
+
click.echo(f"Deleting group: {name}")
|
|
71
|
+
click.echo("-" * 10)
|
|
72
|
+
confirm = confirm or logical_prompt(ctx, "Please type 'delete' to confirm deletion")
|
|
73
|
+
if confirm != "delete":
|
|
74
|
+
raise click.ClickException(
|
|
75
|
+
"Deletion not confirmed. The confirmation word is 'delete'."
|
|
76
|
+
)
|
|
77
|
+
try:
|
|
78
|
+
server: TabsdataServer = ctx.obj["tabsdataserver"]
|
|
79
|
+
server.delete_group(name)
|
|
80
|
+
click.echo("Group deleted successfully")
|
|
81
|
+
except Exception as e:
|
|
82
|
+
hint_common_solutions(ctx, e)
|
|
83
|
+
raise click.ClickException(f"Failed to delete group: {e}")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@group.command()
|
|
87
|
+
@proj_option
|
|
88
|
+
@format_option
|
|
89
|
+
@click.option("--name", "-n", help="Name of the group to display.")
|
|
90
|
+
@click.pass_context
|
|
91
|
+
def get(ctx: click.Context, name: str):
|
|
92
|
+
"""Display a group by name"""
|
|
93
|
+
verify_login_or_prompt(ctx)
|
|
94
|
+
name = name or logical_prompt(ctx, "Name of the group to display")
|
|
95
|
+
try:
|
|
96
|
+
server: TabsdataServer = ctx.obj["tabsdataserver"]
|
|
97
|
+
grp = server.get_group(name)
|
|
98
|
+
collection_names = sorted(
|
|
99
|
+
c.name
|
|
100
|
+
for c in server.list_collections()
|
|
101
|
+
if (c._data or {}).get("group") == name
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
def _table():
|
|
105
|
+
table = Table(title="Group", show_header=False, show_lines=True)
|
|
106
|
+
table.add_column("Field", style="cyan", no_wrap=True)
|
|
107
|
+
table.add_column("Value")
|
|
108
|
+
table.add_row("Name:", grp.name)
|
|
109
|
+
table.add_row("Description:", grp.description or "")
|
|
110
|
+
table.add_row("System:", "Yes" if grp.system else "No")
|
|
111
|
+
table.add_row("Created by:", grp.created_by or "-")
|
|
112
|
+
table.add_row("Created on:", grp.created_on_str or "-")
|
|
113
|
+
table.add_row("Collections:", beautify_list(collection_names))
|
|
114
|
+
click.echo()
|
|
115
|
+
make_console(emoji=False, markup=False).print(table)
|
|
116
|
+
click.echo()
|
|
117
|
+
|
|
118
|
+
render(ctx, data=lambda: grp._data, table=_table)
|
|
119
|
+
except Exception as e:
|
|
120
|
+
hint_common_solutions(ctx, e)
|
|
121
|
+
raise click.ClickException(f"Failed to display group: {e}")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@group.command()
|
|
125
|
+
@proj_option
|
|
126
|
+
@format_option
|
|
127
|
+
@max_result_option
|
|
128
|
+
@click.pass_context
|
|
129
|
+
def list(ctx: click.Context):
|
|
130
|
+
"""List all groups"""
|
|
131
|
+
verify_login_or_prompt(ctx)
|
|
132
|
+
try:
|
|
133
|
+
server: TabsdataServer = ctx.obj["tabsdataserver"]
|
|
134
|
+
list_of_groups, truncated = take(ctx, server.list_groups_generator())
|
|
135
|
+
|
|
136
|
+
def _table():
|
|
137
|
+
table = Table(title="Groups")
|
|
138
|
+
table.add_column("Name", style="cyan", no_wrap=True)
|
|
139
|
+
table.add_column("Description")
|
|
140
|
+
table.add_column("Created by")
|
|
141
|
+
table.add_column("Created on")
|
|
142
|
+
for grp in list_of_groups:
|
|
143
|
+
table.add_row(
|
|
144
|
+
grp.name,
|
|
145
|
+
grp.description,
|
|
146
|
+
grp.created_by,
|
|
147
|
+
grp.created_on_str,
|
|
148
|
+
)
|
|
149
|
+
click.echo()
|
|
150
|
+
make_console().print(table)
|
|
151
|
+
click.echo(f"Number of groups: {len(list_of_groups)}")
|
|
152
|
+
click.echo()
|
|
153
|
+
|
|
154
|
+
render(ctx, data=lambda: [g._data for g in list_of_groups], table=_table)
|
|
155
|
+
echo_more_results(ctx, truncated)
|
|
156
|
+
except Exception as e:
|
|
157
|
+
hint_common_solutions(ctx, e)
|
|
158
|
+
raise click.ClickException(f"Failed to list groups: {e}")
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@group.command()
|
|
162
|
+
@proj_option
|
|
163
|
+
@click.option("--name", "-n", help="Name of the group to update.")
|
|
164
|
+
@click.option("--new-name", help="New name for the group.")
|
|
165
|
+
@click.option("--description", help="New description for the group.")
|
|
166
|
+
@click.pass_context
|
|
167
|
+
def update(
|
|
168
|
+
ctx: click.Context,
|
|
169
|
+
name: str,
|
|
170
|
+
new_name: str,
|
|
171
|
+
description: str,
|
|
172
|
+
):
|
|
173
|
+
"""Update a group by name. System groups cannot be updated."""
|
|
174
|
+
verify_login_or_prompt(ctx)
|
|
175
|
+
name = name or logical_prompt(ctx, "Name of the group to update")
|
|
176
|
+
click.echo(f"Updating group: {name}")
|
|
177
|
+
click.echo("-" * 10)
|
|
178
|
+
try:
|
|
179
|
+
server: TabsdataServer = ctx.obj["tabsdataserver"]
|
|
180
|
+
server.update_group(name, new_name=new_name, new_description=description)
|
|
181
|
+
click.echo("Group updated successfully")
|
|
182
|
+
except Exception as e:
|
|
183
|
+
hint_common_solutions(ctx, e)
|
|
184
|
+
raise click.ClickException(f"Failed to update group: {e}")
|