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,200 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2026 Tabsdata Inc.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import rich_click as click
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
|
|
9
|
+
from tabsdatak.cli._registry import cli_command
|
|
10
|
+
from tabsdatak.cli.cli_utils import (
|
|
11
|
+
echo_more_results,
|
|
12
|
+
format_option,
|
|
13
|
+
hint_common_solutions,
|
|
14
|
+
logical_prompt,
|
|
15
|
+
make_console,
|
|
16
|
+
max_result_option,
|
|
17
|
+
render,
|
|
18
|
+
take,
|
|
19
|
+
verify_login_or_prompt,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@cli_command
|
|
24
|
+
@click.group()
|
|
25
|
+
def user():
|
|
26
|
+
"""User management commands"""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@user.command()
|
|
30
|
+
@click.option("--name", "-n", help="Name of the user to create.")
|
|
31
|
+
@click.option("--full-name", "-f", help="Full name of the user.")
|
|
32
|
+
@click.option("--email", "-e", help="Email of the user.")
|
|
33
|
+
@click.option(
|
|
34
|
+
"--password",
|
|
35
|
+
"-p",
|
|
36
|
+
help="Password for the user. Will be prompted for it if not provided.",
|
|
37
|
+
)
|
|
38
|
+
@click.option("--disabled", is_flag=True, default=False, help="Disable the user.")
|
|
39
|
+
@click.pass_context
|
|
40
|
+
def create(
|
|
41
|
+
ctx: click.Context,
|
|
42
|
+
name: str,
|
|
43
|
+
full_name: str,
|
|
44
|
+
email: str,
|
|
45
|
+
password: str,
|
|
46
|
+
disabled: bool,
|
|
47
|
+
):
|
|
48
|
+
"""Create a new user"""
|
|
49
|
+
verify_login_or_prompt(ctx)
|
|
50
|
+
click.echo("Creating a new user")
|
|
51
|
+
click.echo("-" * 10)
|
|
52
|
+
name = name or logical_prompt(ctx, "Name of the user")
|
|
53
|
+
if password is None:
|
|
54
|
+
password = logical_prompt(ctx, "Password for the user", hide_input=True)
|
|
55
|
+
second_password = logical_prompt(
|
|
56
|
+
ctx, "Please re-enter the password", hide_input=True
|
|
57
|
+
)
|
|
58
|
+
if password != second_password:
|
|
59
|
+
raise click.ClickException("Passwords do not match.")
|
|
60
|
+
full_name = full_name or logical_prompt(
|
|
61
|
+
ctx, "Full name of the user", default_value=name
|
|
62
|
+
)
|
|
63
|
+
email = email or logical_prompt(ctx, "Email of the user", default_value="")
|
|
64
|
+
try:
|
|
65
|
+
ctx.obj["tabsdataserver"].create_user(
|
|
66
|
+
name,
|
|
67
|
+
password,
|
|
68
|
+
full_name=full_name,
|
|
69
|
+
email=email if email else None,
|
|
70
|
+
enabled=not disabled,
|
|
71
|
+
)
|
|
72
|
+
click.echo("User created successfully")
|
|
73
|
+
except Exception as e:
|
|
74
|
+
hint_common_solutions(ctx, e)
|
|
75
|
+
raise click.ClickException(f"Failed to create user: {e}")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@user.command()
|
|
79
|
+
@click.option("--name", "-n", help="Name of the user to delete.")
|
|
80
|
+
@click.option(
|
|
81
|
+
"--confirm",
|
|
82
|
+
help="Write 'delete' to confirm deletion. Will be prompted for it if not provided.",
|
|
83
|
+
)
|
|
84
|
+
@click.pass_context
|
|
85
|
+
def delete(ctx: click.Context, name: str, confirm: str):
|
|
86
|
+
"""Delete a user by name"""
|
|
87
|
+
verify_login_or_prompt(ctx)
|
|
88
|
+
name = name or logical_prompt(ctx, "Name of the user to delete")
|
|
89
|
+
click.echo(f"Deleting user: {name}")
|
|
90
|
+
click.echo("-" * 10)
|
|
91
|
+
confirm = confirm or logical_prompt(ctx, "Please type 'delete' to confirm deletion")
|
|
92
|
+
if confirm != "delete":
|
|
93
|
+
raise click.ClickException(
|
|
94
|
+
"Deletion not confirmed. The confirmation word is 'delete'."
|
|
95
|
+
)
|
|
96
|
+
try:
|
|
97
|
+
ctx.obj["tabsdataserver"].delete_user(name)
|
|
98
|
+
click.echo("User deleted successfully")
|
|
99
|
+
except Exception as e:
|
|
100
|
+
hint_common_solutions(ctx, e)
|
|
101
|
+
raise click.ClickException(f"Failed to delete user: {e}")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@user.command()
|
|
105
|
+
@format_option
|
|
106
|
+
@click.option("--name", "-n", help="Name of the user to display.")
|
|
107
|
+
@click.pass_context
|
|
108
|
+
def get(ctx: click.Context, name: str):
|
|
109
|
+
"""Display a user by name"""
|
|
110
|
+
verify_login_or_prompt(ctx)
|
|
111
|
+
name = name or logical_prompt(ctx, "Name of the user to display")
|
|
112
|
+
try:
|
|
113
|
+
user = ctx.obj["tabsdataserver"].get_user(name)
|
|
114
|
+
|
|
115
|
+
def _table():
|
|
116
|
+
table = Table(title="User")
|
|
117
|
+
table.add_column("Name", style="cyan", no_wrap=True)
|
|
118
|
+
table.add_column("Full name")
|
|
119
|
+
table.add_column("Email")
|
|
120
|
+
table.add_column("Enabled")
|
|
121
|
+
table.add_row(user.name, user.full_name, user.email, str(user.enabled))
|
|
122
|
+
click.echo()
|
|
123
|
+
make_console().print(table)
|
|
124
|
+
click.echo()
|
|
125
|
+
|
|
126
|
+
render(ctx, data=lambda: user._data, table=_table)
|
|
127
|
+
except Exception as e:
|
|
128
|
+
hint_common_solutions(ctx, e)
|
|
129
|
+
raise click.ClickException(f"Failed to display user: {e}")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@user.command()
|
|
133
|
+
@format_option
|
|
134
|
+
@max_result_option
|
|
135
|
+
@click.pass_context
|
|
136
|
+
def list(ctx: click.Context):
|
|
137
|
+
"""List all users"""
|
|
138
|
+
verify_login_or_prompt(ctx)
|
|
139
|
+
try:
|
|
140
|
+
list_of_users, truncated = take(
|
|
141
|
+
ctx, ctx.obj["tabsdataserver"].list_users_generator()
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
def _table():
|
|
145
|
+
table = Table(title="Users")
|
|
146
|
+
table.add_column("Name", style="cyan", no_wrap=True)
|
|
147
|
+
table.add_column("Full name")
|
|
148
|
+
table.add_column("Email")
|
|
149
|
+
table.add_column("Enabled")
|
|
150
|
+
for user in list_of_users:
|
|
151
|
+
table.add_row(user.name, user.full_name, user.email, str(user.enabled))
|
|
152
|
+
click.echo()
|
|
153
|
+
make_console().print(table)
|
|
154
|
+
click.echo(f"Number of users: {len(list_of_users)}")
|
|
155
|
+
click.echo()
|
|
156
|
+
|
|
157
|
+
render(ctx, data=lambda: [u._data for u in list_of_users], table=_table)
|
|
158
|
+
echo_more_results(ctx, truncated)
|
|
159
|
+
except Exception as e:
|
|
160
|
+
hint_common_solutions(ctx, e)
|
|
161
|
+
raise click.ClickException(f"Failed to list users: {e}")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@user.command()
|
|
165
|
+
@click.option("--name", "-n", help="Name of the user to update.")
|
|
166
|
+
@click.option("--full-name", "-f", help="New full name of the user.")
|
|
167
|
+
@click.option("--email", "-e", help="New email of the user.")
|
|
168
|
+
@click.option("--password", "-p", help="New password for the user.")
|
|
169
|
+
@click.option(
|
|
170
|
+
"--enabled",
|
|
171
|
+
type=bool,
|
|
172
|
+
default=None,
|
|
173
|
+
help="Indicate if the user is enabled or disabled",
|
|
174
|
+
)
|
|
175
|
+
@click.pass_context
|
|
176
|
+
def update(
|
|
177
|
+
ctx: click.Context,
|
|
178
|
+
name: str,
|
|
179
|
+
full_name: str,
|
|
180
|
+
email: str,
|
|
181
|
+
password: str,
|
|
182
|
+
enabled: bool,
|
|
183
|
+
):
|
|
184
|
+
"""Update a user by name"""
|
|
185
|
+
verify_login_or_prompt(ctx)
|
|
186
|
+
name = name or logical_prompt(ctx, "Name of the user to update")
|
|
187
|
+
click.echo(f"Updating user: {name}")
|
|
188
|
+
click.echo("-" * 10)
|
|
189
|
+
try:
|
|
190
|
+
ctx.obj["tabsdataserver"].update_user(
|
|
191
|
+
name,
|
|
192
|
+
full_name=full_name,
|
|
193
|
+
email=email,
|
|
194
|
+
password=password,
|
|
195
|
+
enabled=enabled,
|
|
196
|
+
)
|
|
197
|
+
click.echo("User updated successfully")
|
|
198
|
+
except Exception as e:
|
|
199
|
+
hint_common_solutions(ctx, e)
|
|
200
|
+
raise click.ClickException(f"Failed to update user: {e}")
|
|
@@ -0,0 +1,185 @@
|
|
|
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
|
+
|
|
11
|
+
from tabsdatak.cli._registry import cli_command
|
|
12
|
+
from tabsdatak.cli.cli_utils import (
|
|
13
|
+
echo_more_results,
|
|
14
|
+
format_option,
|
|
15
|
+
hint_common_solutions,
|
|
16
|
+
make_console,
|
|
17
|
+
max_result_option,
|
|
18
|
+
output_format,
|
|
19
|
+
render,
|
|
20
|
+
show_hint,
|
|
21
|
+
take,
|
|
22
|
+
verify_login_or_prompt,
|
|
23
|
+
)
|
|
24
|
+
from tabsdatak.cli.td.execution_utils import (
|
|
25
|
+
echo_function_run_status,
|
|
26
|
+
echo_plan_status,
|
|
27
|
+
echo_transaction_status,
|
|
28
|
+
emit_worker_logs,
|
|
29
|
+
obtain_list_worker_filters,
|
|
30
|
+
span_projects,
|
|
31
|
+
workers_table,
|
|
32
|
+
)
|
|
33
|
+
from tabsdatak.sdk.status_utils.worker import WORKER_VALID_USER_PROVIDED_STATUSES
|
|
34
|
+
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from tabsdatak.sdk.tabsdata_server import TabsdataServer
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@cli_command
|
|
40
|
+
@click.group()
|
|
41
|
+
def worker():
|
|
42
|
+
"""Worker management commands"""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@worker.command(name="list")
|
|
46
|
+
@click.option("--proj", type=str, help="Name of the project to filter workers by.")
|
|
47
|
+
@click.option(
|
|
48
|
+
"--status",
|
|
49
|
+
type=str,
|
|
50
|
+
multiple=True,
|
|
51
|
+
help=(
|
|
52
|
+
"Filter workers by status. The possible statuses are "
|
|
53
|
+
f"'{WORKER_VALID_USER_PROVIDED_STATUSES}'. This field is "
|
|
54
|
+
"case-insensitive. "
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
@click.option(
|
|
58
|
+
"--coll",
|
|
59
|
+
type=str,
|
|
60
|
+
help="Collection of the function to which the workers belong.",
|
|
61
|
+
)
|
|
62
|
+
@click.option(
|
|
63
|
+
"--fn", type=str, help="Name of the function to which the workers belong."
|
|
64
|
+
)
|
|
65
|
+
@click.option("--plan", type=str, help="ID of the plan to filter workers by.")
|
|
66
|
+
@click.option(
|
|
67
|
+
"--plan-name",
|
|
68
|
+
help=(
|
|
69
|
+
"A plan name wildcard to match. "
|
|
70
|
+
"For example, 'my_plan*' will match all plans "
|
|
71
|
+
"with names starting with 'my_plan'."
|
|
72
|
+
),
|
|
73
|
+
)
|
|
74
|
+
@click.option("--fnrun", type=str, help="ID of the function run to filter workers by.")
|
|
75
|
+
@click.option("--trx", type=str, help="ID of the transaction to filter workers by.")
|
|
76
|
+
@format_option
|
|
77
|
+
@max_result_option
|
|
78
|
+
@click.pass_context
|
|
79
|
+
def list_workers(
|
|
80
|
+
ctx: click.Context,
|
|
81
|
+
proj: str,
|
|
82
|
+
status: List[str],
|
|
83
|
+
coll: str,
|
|
84
|
+
fn: str,
|
|
85
|
+
plan: str,
|
|
86
|
+
plan_name: str,
|
|
87
|
+
fnrun: str,
|
|
88
|
+
trx: str,
|
|
89
|
+
):
|
|
90
|
+
"""List workers across every project you can view."""
|
|
91
|
+
verify_login_or_prompt(ctx)
|
|
92
|
+
try:
|
|
93
|
+
server: TabsdataServer = ctx.obj["tabsdataserver"]
|
|
94
|
+
span_projects(server)
|
|
95
|
+
request_filter = obtain_list_worker_filters(
|
|
96
|
+
status=status,
|
|
97
|
+
exe_name=plan_name,
|
|
98
|
+
execution_id=plan,
|
|
99
|
+
fn=fn,
|
|
100
|
+
fnrun=fnrun,
|
|
101
|
+
collection=coll,
|
|
102
|
+
trx=trx,
|
|
103
|
+
project=proj,
|
|
104
|
+
)
|
|
105
|
+
if output_format(ctx) == "table":
|
|
106
|
+
if plan:
|
|
107
|
+
echo_plan_status(server, plan)
|
|
108
|
+
if trx:
|
|
109
|
+
echo_transaction_status(server, trx)
|
|
110
|
+
if fnrun:
|
|
111
|
+
echo_function_run_status(server, fnrun)
|
|
112
|
+
list_of_workers, truncated = take(
|
|
113
|
+
ctx, server.list_workers_generator(filter=request_filter)
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
if output_format(ctx) != "table":
|
|
117
|
+
render(
|
|
118
|
+
ctx,
|
|
119
|
+
data=[w._data for w in list_of_workers],
|
|
120
|
+
table=lambda: None,
|
|
121
|
+
)
|
|
122
|
+
echo_more_results(ctx, truncated)
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
click.echo()
|
|
126
|
+
console = make_console()
|
|
127
|
+
console.print(workers_table(list_of_workers))
|
|
128
|
+
click.echo(f"Number of workers: {len(list_of_workers)}")
|
|
129
|
+
echo_more_results(ctx, truncated)
|
|
130
|
+
click.echo()
|
|
131
|
+
show_hint(
|
|
132
|
+
ctx,
|
|
133
|
+
"Read logs with 'tdk worker logs --worker <WORKER-ID>', or "
|
|
134
|
+
"'tdk fnrun logs --fnrun <RUN-ID>' for a function run's workers.",
|
|
135
|
+
)
|
|
136
|
+
except Exception as e:
|
|
137
|
+
hint_common_solutions(ctx, e)
|
|
138
|
+
raise click.ClickException(f"Failed to list workers: {e}")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@worker.command()
|
|
142
|
+
@click.option("--worker", required=True, help="ID of the worker whose logs to show.")
|
|
143
|
+
@click.option(
|
|
144
|
+
"--file",
|
|
145
|
+
help=(
|
|
146
|
+
"Path of the file where the logs will be saved, or '-' to write them to "
|
|
147
|
+
"stdout. Any folders provided before the file must already exist."
|
|
148
|
+
),
|
|
149
|
+
)
|
|
150
|
+
@format_option
|
|
151
|
+
@click.pass_context
|
|
152
|
+
def logs(ctx: click.Context, worker: str, file: str):
|
|
153
|
+
"""
|
|
154
|
+
Show the logs of a worker.
|
|
155
|
+
"""
|
|
156
|
+
verify_login_or_prompt(ctx)
|
|
157
|
+
try:
|
|
158
|
+
server: TabsdataServer = ctx.obj["tabsdataserver"]
|
|
159
|
+
emit_worker_logs(ctx, server, worker, file)
|
|
160
|
+
except Exception as e:
|
|
161
|
+
hint_common_solutions(ctx, e)
|
|
162
|
+
raise click.ClickException(f"Failed to show logs: {e}")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@worker.command()
|
|
166
|
+
@click.option("--worker", required=True, help="ID of the worker to recover.")
|
|
167
|
+
@click.pass_context
|
|
168
|
+
def recover(ctx: click.Context, worker: str):
|
|
169
|
+
"""
|
|
170
|
+
Recover the transaction a worker belongs to: the transaction's failed or
|
|
171
|
+
on-hold function runs are re-scheduled.
|
|
172
|
+
"""
|
|
173
|
+
verify_login_or_prompt(ctx)
|
|
174
|
+
try:
|
|
175
|
+
server: TabsdataServer = ctx.obj["tabsdataserver"]
|
|
176
|
+
span_projects(server)
|
|
177
|
+
target = server.get_worker(worker)
|
|
178
|
+
trx = target.transaction.id
|
|
179
|
+
click.echo(f"Recovering transaction '{trx}' of worker '{worker}'")
|
|
180
|
+
click.echo("-" * 10)
|
|
181
|
+
server.recover_transaction(trx)
|
|
182
|
+
click.echo("Transaction recovered successfully")
|
|
183
|
+
except Exception as e:
|
|
184
|
+
hint_common_solutions(ctx, e)
|
|
185
|
+
raise click.ClickException(f"Failed to recover worker: {e}")
|