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.
Files changed (34) hide show
  1. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/PKG-INFO +6 -8
  2. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/README-public.md +2 -4
  3. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/__init__.py +9 -3
  4. cmem_cmemc-23.3.0/cmem/cmemc/cli/_cmemc.zsh +44 -0
  5. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/__init__.py +3 -0
  6. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/admin.py +3 -1
  7. cmem_cmemc-23.3.0/cmem/cmemc/cli/commands/client.py +173 -0
  8. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/config.py +1 -1
  9. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/dataset.py +161 -70
  10. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/graph.py +10 -10
  11. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/metrics.py +3 -3
  12. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/project.py +90 -27
  13. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/python.py +110 -29
  14. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/query.py +6 -6
  15. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/resource.py +5 -5
  16. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/scheduler.py +4 -4
  17. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/store.py +34 -31
  18. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/user.py +27 -10
  19. cmem_cmemc-23.3.0/cmem/cmemc/cli/commands/variable.py +364 -0
  20. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/vocabulary.py +5 -5
  21. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/workflow.py +118 -55
  22. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/commands/workspace.py +5 -5
  23. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/completion.py +393 -154
  24. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/context.py +20 -3
  25. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/manual_helper/multi_page.py +11 -6
  26. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/utils.py +80 -0
  27. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/pyproject.toml +9 -6
  28. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/LICENSE +0 -0
  29. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/__init__.py +0 -0
  30. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/__init__.py +0 -0
  31. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/exceptions.py +0 -0
  32. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/manual_helper/__init__.py +0 -0
  33. {cmem_cmemc-23.1.3 → cmem_cmemc-23.3.0}/cmem/cmemc/cli/manual_helper/graph.py +0 -0
  34. {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.1.3
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.9
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 (==7.1.2)
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.1.1)
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
- pip install cmem-cmemc
53
-
54
- You may consider installing cmemc only for the current user:
52
+ pipx install cmem-cmemc
55
53
 
56
- pip install cmem-cmemc --user
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
- pip install cmem-cmemc
9
+ pipx install cmem-cmemc
10
10
 
11
- You may consider installing cmemc only for the current user:
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
- autocompletion=completion.connections,
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
- autocompletion=completion.ini_files,
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
- autocompletion=completion.status_keys,
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)
@@ -41,7 +41,7 @@ def edit_command(app):
41
41
  "KEY",
42
42
  nargs=1,
43
43
  type=click.Choice(
44
- KNOWN_CONFIG_KEYS.keys(),
44
+ list(KNOWN_CONFIG_KEYS.keys()),
45
45
  case_sensitive=False
46
46
  )
47
47
  )