cmem-cmemc 23.1.3__tar.gz → 23.3.0__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.
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/PKG-INFO +6 -8
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/README-public.md +2 -4
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/__init__.py +9 -3
- cmem_cmemc-23.3.0/cmem/cmemc/cli/_cmemc.zsh +44 -0
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/__init__.py +3 -0
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/admin.py +3 -1
- cmem_cmemc-23.3.0/cmem/cmemc/cli/commands/client.py +173 -0
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/config.py +1 -1
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/dataset.py +161 -70
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/graph.py +10 -10
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/metrics.py +3 -3
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/project.py +90 -27
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/python.py +110 -29
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/query.py +6 -6
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/resource.py +5 -5
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/scheduler.py +4 -4
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/store.py +34 -31
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/user.py +27 -10
- cmem_cmemc-23.3.0/cmem/cmemc/cli/commands/variable.py +364 -0
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/vocabulary.py +5 -5
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/workflow.py +118 -55
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/workspace.py +5 -5
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/completion.py +393 -154
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/context.py +20 -3
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/manual_helper/multi_page.py +11 -6
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/utils.py +80 -0
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/pyproject.toml +9 -6
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/LICENSE +0 -0
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/__init__.py +0 -0
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/__init__.py +0 -0
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/exceptions.py +0 -0
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/manual_helper/__init__.py +0 -0
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/manual_helper/graph.py +0 -0
- {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/manual_helper/single_page.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cmem-cmemc
|
|
3
|
-
Version: 23.
|
|
3
|
+
Version: 23.3.0
|
|
4
4
|
Summary: Command line client for eccenca Corporate Memory
|
|
5
5
|
Home-page: https://eccenca.com/go/cmemc
|
|
6
6
|
License: Apache 2.0
|
|
@@ -17,16 +17,16 @@ Classifier: Programming Language :: Python :: 3
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.9
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
21
|
Classifier: Topic :: Database
|
|
22
22
|
Classifier: Topic :: Software Development :: Testing
|
|
23
23
|
Classifier: Topic :: Utilities
|
|
24
24
|
Requires-Dist: beautifulsoup4 (>=4.12.2,<5.0.0)
|
|
25
25
|
Requires-Dist: certifi (>=2023.5.7)
|
|
26
|
-
Requires-Dist: click (
|
|
26
|
+
Requires-Dist: click (>=8.1.7,<9.0.0)
|
|
27
27
|
Requires-Dist: click-didyoumean (>=0.3.0,<0.4.0)
|
|
28
28
|
Requires-Dist: click-help-colors (>=0.9.1,<0.10.0)
|
|
29
|
-
Requires-Dist: cmem-cmempy (==23.
|
|
29
|
+
Requires-Dist: cmem-cmempy (==23.3.0)
|
|
30
30
|
Requires-Dist: configparser (>=5.3.0,<6.0.0)
|
|
31
31
|
Requires-Dist: jinja2 (>=3.1.2,<4.0.0)
|
|
32
32
|
Requires-Dist: natsort (>=8.3.1,<9.0.0)
|
|
@@ -49,11 +49,9 @@ cmemc is the official command line client for [eccenca Corporate Memory](https:/
|
|
|
49
49
|
|
|
50
50
|
In order to install the cmemc, run:
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
You may consider installing cmemc only for the current user:
|
|
52
|
+
pipx install cmem-cmemc
|
|
55
53
|
|
|
56
|
-
|
|
54
|
+
Of course you can install cmemc also with pip, but we recommend [pipx](https://pypa.github.io/pipx/) for normal desktop usage.
|
|
57
55
|
|
|
58
56
|
## Configuration and Usage
|
|
59
57
|
|
|
@@ -6,11 +6,9 @@ cmemc is the official command line client for [eccenca Corporate Memory](https:/
|
|
|
6
6
|
|
|
7
7
|
In order to install the cmemc, run:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
pipx install cmem-cmemc
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
pip install cmem-cmemc --user
|
|
11
|
+
Of course you can install cmemc also with pip, but we recommend [pipx](https://pypa.github.io/pipx/) for normal desktop usage.
|
|
14
12
|
|
|
15
13
|
## Configuration and Usage
|
|
16
14
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""The main command line interface."""
|
|
2
|
-
|
|
2
|
+
from importlib.resources import open_text
|
|
3
3
|
import os
|
|
4
4
|
from subprocess import CalledProcessError # nosec
|
|
5
5
|
import sys
|
|
@@ -38,6 +38,12 @@ from cmem.cmemc.cli.commands import CmemcGroup
|
|
|
38
38
|
|
|
39
39
|
CMEMC_VERSION = get_version()
|
|
40
40
|
|
|
41
|
+
# this will output a custom zsh completion function
|
|
42
|
+
if os.environ.get("_CMEMC_COMPLETE", "") == "zsh_source":
|
|
43
|
+
with open_text("cmem.cmemc.cli", "_cmemc.zsh") as zsh_output:
|
|
44
|
+
print(zsh_output.read())
|
|
45
|
+
sys.exit(0)
|
|
46
|
+
|
|
41
47
|
version = sys.version_info
|
|
42
48
|
PYTHON_VERSION = f"{version.major}.{version.minor}.{version.micro}"
|
|
43
49
|
PYTHON_EXPECTED = "3.11"
|
|
@@ -65,12 +71,12 @@ CONTEXT_SETTINGS = {
|
|
|
65
71
|
@click.option(
|
|
66
72
|
'-c', '--connection',
|
|
67
73
|
type=click.STRING,
|
|
68
|
-
|
|
74
|
+
shell_complete=completion.connections,
|
|
69
75
|
help='Use a specific connection from the config file.'
|
|
70
76
|
)
|
|
71
77
|
@click.option(
|
|
72
78
|
'--config-file',
|
|
73
|
-
|
|
79
|
+
shell_complete=completion.ini_files,
|
|
74
80
|
type=click.Path(
|
|
75
81
|
readable=True,
|
|
76
82
|
allow_dash=False,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#compdef cmemc
|
|
2
|
+
|
|
3
|
+
_cmemc_completion() {
|
|
4
|
+
local -a completions
|
|
5
|
+
local -a completions_with_descriptions
|
|
6
|
+
local -a response
|
|
7
|
+
(( ! $+commands[cmemc] )) && return 1
|
|
8
|
+
|
|
9
|
+
response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) _CMEMC_COMPLETE=zsh_complete cmemc)}")
|
|
10
|
+
|
|
11
|
+
for type key descr in ${response}; do
|
|
12
|
+
if [[ "$type" == "plain" ]]; then
|
|
13
|
+
if [[ "$descr" == "_" ]]; then
|
|
14
|
+
completions+=("$key")
|
|
15
|
+
else
|
|
16
|
+
completions_with_descriptions+=("$key":"$descr")
|
|
17
|
+
fi
|
|
18
|
+
elif [[ "$type" == "dir" ]]; then
|
|
19
|
+
_path_files -/
|
|
20
|
+
elif [[ "$type" == "file" ]]; then
|
|
21
|
+
_path_files -f
|
|
22
|
+
fi
|
|
23
|
+
done
|
|
24
|
+
|
|
25
|
+
if [ -n "$completions_with_descriptions" ]; then
|
|
26
|
+
_describe -V unsorted completions_with_descriptions -U
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
if [ -n "$completions" ]; then
|
|
30
|
+
compadd -U -V unsorted -a completions
|
|
31
|
+
fi
|
|
32
|
+
# Other than the standard click completion function, we re-added this line
|
|
33
|
+
# in order to have working completions for task IDs
|
|
34
|
+
compstate[insert]="automenu"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if [[ $zsh_eval_context[-1] == loadautofunc ]]; then
|
|
38
|
+
# autoload from fpath, call function directly
|
|
39
|
+
_cmemc_completion "$@"
|
|
40
|
+
else
|
|
41
|
+
# eval/source/. command, register function for later
|
|
42
|
+
compdef _cmemc_completion cmemc
|
|
43
|
+
fi
|
|
44
|
+
|
|
@@ -29,6 +29,7 @@ class CmemcGroup(HelpColorsGroup, DYMGroup):
|
|
|
29
29
|
"showcase": COLOR_FOR_WRITING_COMMANDS,
|
|
30
30
|
"delete": COLOR_FOR_WRITING_COMMANDS,
|
|
31
31
|
"password": COLOR_FOR_WRITING_COMMANDS,
|
|
32
|
+
"secret": COLOR_FOR_WRITING_COMMANDS,
|
|
32
33
|
"upload": COLOR_FOR_WRITING_COMMANDS,
|
|
33
34
|
"import": COLOR_FOR_WRITING_COMMANDS,
|
|
34
35
|
"create": COLOR_FOR_WRITING_COMMANDS,
|
|
@@ -59,6 +60,8 @@ class CmemcGroup(HelpColorsGroup, DYMGroup):
|
|
|
59
60
|
"python": COLOR_FOR_COMMAND_GROUPS,
|
|
60
61
|
"cache": COLOR_FOR_COMMAND_GROUPS,
|
|
61
62
|
"resource": COLOR_FOR_COMMAND_GROUPS,
|
|
63
|
+
"client": COLOR_FOR_COMMAND_GROUPS,
|
|
64
|
+
"variable": COLOR_FOR_COMMAND_GROUPS,
|
|
62
65
|
}
|
|
63
66
|
)
|
|
64
67
|
super().__init__(*args, **kwargs)
|
|
@@ -4,6 +4,7 @@ import click
|
|
|
4
4
|
import jwt
|
|
5
5
|
|
|
6
6
|
from cmem.cmemc.cli import completion
|
|
7
|
+
from cmem.cmemc.cli.commands.client import client
|
|
7
8
|
from cmem.cmemc.cli.commands.user import user
|
|
8
9
|
from cmem.cmemc.cli.context import ApplicationContext
|
|
9
10
|
from cmem.cmemc.cli.utils import struct_to_table
|
|
@@ -20,7 +21,7 @@ from cmem.cmempy.health import get_complete_status_info
|
|
|
20
21
|
@click.command(cls=CmemcCommand, name="status")
|
|
21
22
|
@click.option(
|
|
22
23
|
"--key", "key",
|
|
23
|
-
|
|
24
|
+
shell_complete=completion.status_keys,
|
|
24
25
|
help="Get only specific key(s) from the status / info output. There are "
|
|
25
26
|
"two special keys available: 'all' will list all available keys in "
|
|
26
27
|
"the table, 'overall.healthy' with result in UP in case all "
|
|
@@ -192,3 +193,4 @@ admin.add_command(metrics)
|
|
|
192
193
|
admin.add_command(workspace)
|
|
193
194
|
admin.add_command(store)
|
|
194
195
|
admin.add_command(user)
|
|
196
|
+
admin.add_command(client)
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Keycloak client management commands"""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from cmem.cmemc.cli import completion
|
|
6
|
+
from cmem.cmemc.cli.commands import CmemcCommand, CmemcGroup
|
|
7
|
+
from cmem.cmemc.cli.context import ApplicationContext
|
|
8
|
+
from cmem.cmempy.config import get_keycloak_base_uri, get_keycloak_realm_id
|
|
9
|
+
from cmem.cmempy.keycloak.client import (
|
|
10
|
+
get_client_by_client_id,
|
|
11
|
+
generate_client_secret,
|
|
12
|
+
get_client_secret, list_open_id_clients
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
NO_CLIENT_ERROR = "{} is not a valid client account. " \
|
|
16
|
+
"Use the 'admin client list' command " \
|
|
17
|
+
"to get a list of existing client accounts."
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.command(cls=CmemcCommand, name="list")
|
|
21
|
+
@click.option(
|
|
22
|
+
"--raw",
|
|
23
|
+
is_flag=True,
|
|
24
|
+
help="Outputs raw JSON."
|
|
25
|
+
)
|
|
26
|
+
@click.option(
|
|
27
|
+
"--id-only",
|
|
28
|
+
is_flag=True,
|
|
29
|
+
help="Lists only Client ID. "
|
|
30
|
+
"This is useful for piping the IDs into other commands."
|
|
31
|
+
)
|
|
32
|
+
@click.pass_obj
|
|
33
|
+
def list_command(app: ApplicationContext, raw, id_only):
|
|
34
|
+
"""
|
|
35
|
+
List client accounts.
|
|
36
|
+
|
|
37
|
+
Outputs a list of client accounts, which can be used to get an overview as well
|
|
38
|
+
as a reference for the other commands of the `admin client` command group.
|
|
39
|
+
|
|
40
|
+
Note: The list command only outputs clients which have a client secret.
|
|
41
|
+
Use the `--raw` option to get a JSON description of all clients.
|
|
42
|
+
"""
|
|
43
|
+
clients = list_open_id_clients()
|
|
44
|
+
if raw:
|
|
45
|
+
app.echo_info_json(clients)
|
|
46
|
+
return
|
|
47
|
+
if id_only:
|
|
48
|
+
for cnt in clients:
|
|
49
|
+
app.echo_info(cnt["clientId"])
|
|
50
|
+
return
|
|
51
|
+
table = []
|
|
52
|
+
for cnt in clients:
|
|
53
|
+
table.append((
|
|
54
|
+
cnt["clientId"],
|
|
55
|
+
cnt.get("description", "-")
|
|
56
|
+
))
|
|
57
|
+
app.echo_info_table(
|
|
58
|
+
table,
|
|
59
|
+
headers=["Client ID", "Description"],
|
|
60
|
+
sort_column=0
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@click.command(cls=CmemcCommand, name="secret")
|
|
65
|
+
@click.argument(
|
|
66
|
+
"client-id",
|
|
67
|
+
shell_complete=completion.client_ids
|
|
68
|
+
)
|
|
69
|
+
@click.option(
|
|
70
|
+
"--generate",
|
|
71
|
+
is_flag=True,
|
|
72
|
+
help="Generate a new secret"
|
|
73
|
+
)
|
|
74
|
+
@click.option(
|
|
75
|
+
"--output",
|
|
76
|
+
is_flag=True,
|
|
77
|
+
help="Display client secret"
|
|
78
|
+
)
|
|
79
|
+
@click.pass_obj
|
|
80
|
+
def secret_command(app: ApplicationContext, client_id, generate, output):
|
|
81
|
+
"""
|
|
82
|
+
Get or generate a new secret for a client account.
|
|
83
|
+
|
|
84
|
+
This command retrieves or generates a new secret for a client account from a realm.
|
|
85
|
+
"""
|
|
86
|
+
if not output and not generate:
|
|
87
|
+
app.echo_info(click.get_current_context().get_help())
|
|
88
|
+
raise ValueError(
|
|
89
|
+
"You need to use '--output' or '--generate' as an option."
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
clients = get_client_by_client_id(client_id)
|
|
93
|
+
if not clients:
|
|
94
|
+
raise ValueError(NO_CLIENT_ERROR.format(client_id))
|
|
95
|
+
|
|
96
|
+
if generate:
|
|
97
|
+
if not output:
|
|
98
|
+
app.echo_info(
|
|
99
|
+
f"Generating a new secret for {client_id} ... ",
|
|
100
|
+
nl=False
|
|
101
|
+
)
|
|
102
|
+
credential = generate_client_secret(client_id=clients[0]["id"])
|
|
103
|
+
if not output:
|
|
104
|
+
app.echo_success("done")
|
|
105
|
+
else:
|
|
106
|
+
credential = get_client_secret(client_id=clients[0]["id"])
|
|
107
|
+
|
|
108
|
+
if output:
|
|
109
|
+
app.echo_result(credential["value"])
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@click.command(cls=CmemcCommand, name="open")
|
|
113
|
+
@click.argument(
|
|
114
|
+
"client-ids",
|
|
115
|
+
nargs=-1,
|
|
116
|
+
required=False,
|
|
117
|
+
type=click.STRING,
|
|
118
|
+
shell_complete=completion.client_ids
|
|
119
|
+
)
|
|
120
|
+
@click.pass_obj
|
|
121
|
+
def open_command(app: ApplicationContext, client_ids):
|
|
122
|
+
"""Open clients in the browser.
|
|
123
|
+
|
|
124
|
+
With this command, you can open a client in the keycloak web
|
|
125
|
+
interface in your browser.
|
|
126
|
+
|
|
127
|
+
The command accepts multiple client IDs which results in
|
|
128
|
+
opening multiple browser tabs.
|
|
129
|
+
"""
|
|
130
|
+
open_client_base_uri = (
|
|
131
|
+
f"{get_keycloak_base_uri()}/admin/master/console/#/"
|
|
132
|
+
f"{get_keycloak_realm_id()}/clients"
|
|
133
|
+
)
|
|
134
|
+
if not client_ids:
|
|
135
|
+
app.echo_debug(f"Open users list: {open_client_base_uri}")
|
|
136
|
+
click.launch(open_client_base_uri)
|
|
137
|
+
else:
|
|
138
|
+
clients = list_open_id_clients()
|
|
139
|
+
client_id_map = {c["clientId"]: c["id"] for c in clients}
|
|
140
|
+
for _ in client_ids:
|
|
141
|
+
if _ not in client_id_map.keys():
|
|
142
|
+
raise ValueError(NO_CLIENT_ERROR.format(_))
|
|
143
|
+
client_id = client_id_map[_]
|
|
144
|
+
open_user_uri = f"{open_client_base_uri}/{client_id}/settings"
|
|
145
|
+
|
|
146
|
+
app.echo_debug(f"Open {_}: {open_user_uri}")
|
|
147
|
+
click.launch(open_user_uri)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@click.group(cls=CmemcGroup)
|
|
151
|
+
def client():
|
|
152
|
+
"""
|
|
153
|
+
List client accounts, get or generate client account secrets.
|
|
154
|
+
|
|
155
|
+
This command group is an opinionated interface to the Keycloak realm of your
|
|
156
|
+
Corporate Memory instance. In order to be able to use the commands in this group,
|
|
157
|
+
the configured cmemc connection account needs to be equipped with the
|
|
158
|
+
`manage-clients` role in the used realm.
|
|
159
|
+
|
|
160
|
+
Client accounts are identified by a client ID which is unique in the scope of
|
|
161
|
+
the used realm.
|
|
162
|
+
|
|
163
|
+
In case your Corporate Memory deployment does not use the default deployment
|
|
164
|
+
layout, the following additional config variables can be used in your
|
|
165
|
+
connection configuration: `KEYCLOAK_BASE_URI` defaults to
|
|
166
|
+
`{CMEM_BASE_URI}/auth` and locates your Keycloak deployment;
|
|
167
|
+
`KEYCLOAK_REALM_ID` defaults to `cmem` and identifies the used realm.
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
client.add_command(list_command)
|
|
172
|
+
client.add_command(secret_command)
|
|
173
|
+
client.add_command(open_command)
|