lkr-dev-cli 0.0.30__py3-none-any.whl → 0.0.32__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.
- lkr/auth/main.py +17 -5
- lkr/auth_service.py +4 -3
- lkr/logger.py +33 -22
- lkr/main.py +25 -6
- lkr/observability/main.py +2 -0
- lkr/tools/classes.py +5 -4
- lkr/tools/main.py +4 -1
- {lkr_dev_cli-0.0.30.dist-info → lkr_dev_cli-0.0.32.dist-info}/METADATA +107 -22
- {lkr_dev_cli-0.0.30.dist-info → lkr_dev_cli-0.0.32.dist-info}/RECORD +12 -12
- {lkr_dev_cli-0.0.30.dist-info → lkr_dev_cli-0.0.32.dist-info}/WHEEL +0 -0
- {lkr_dev_cli-0.0.30.dist-info → lkr_dev_cli-0.0.32.dist-info}/entry_points.txt +0 -0
- {lkr_dev_cli-0.0.30.dist-info → lkr_dev_cli-0.0.32.dist-info}/licenses/LICENSE +0 -0
lkr/auth/main.py
CHANGED
@@ -1,16 +1,25 @@
|
|
1
1
|
import urllib.parse
|
2
2
|
from typing import Annotated, List, Union
|
3
3
|
|
4
|
-
import questionary
|
5
4
|
import typer
|
6
5
|
from looker_sdk.rtl.auth_token import AccessToken, AuthToken
|
7
|
-
from rich.console import Console
|
8
|
-
from rich.table import Table
|
9
6
|
|
10
7
|
from lkr.auth.oauth import OAuth2PKCE
|
11
8
|
from lkr.auth_service import get_auth
|
12
9
|
from lkr.logger import logger
|
13
10
|
|
11
|
+
QUESTIONARY_AVAILABLE = True
|
12
|
+
RICH_AVAILABLE = True
|
13
|
+
try:
|
14
|
+
import questionary
|
15
|
+
except ModuleNotFoundError:
|
16
|
+
QUESTIONARY_AVAILABLE = False
|
17
|
+
try:
|
18
|
+
from rich.console import Console
|
19
|
+
from rich.table import Table
|
20
|
+
except ModuleNotFoundError:
|
21
|
+
RICH_AVAILABLE = False
|
22
|
+
|
14
23
|
__all__ = ["group"]
|
15
24
|
|
16
25
|
group = typer.Typer(name="auth", help="Authentication commands for LookML Repository")
|
@@ -199,7 +208,7 @@ def list(ctx: typer.Context):
|
|
199
208
|
"""
|
200
209
|
List all authenticated Looker instances
|
201
210
|
"""
|
202
|
-
console = Console()
|
211
|
+
console = Console() if RICH_AVAILABLE else None
|
203
212
|
auth = get_auth(ctx)
|
204
213
|
all_instances = auth.list_auth()
|
205
214
|
if not all_instances:
|
@@ -213,7 +222,10 @@ def list(ctx: typer.Context):
|
|
213
222
|
instance[1],
|
214
223
|
"Yes" if instance[3] else "No",
|
215
224
|
)
|
216
|
-
console
|
225
|
+
if console:
|
226
|
+
console.print(table)
|
227
|
+
else:
|
228
|
+
print(table)
|
217
229
|
|
218
230
|
|
219
231
|
if __name__ == "__main__":
|
lkr/auth_service.py
CHANGED
@@ -3,10 +3,11 @@ import os
|
|
3
3
|
import sqlite3
|
4
4
|
import types
|
5
5
|
from datetime import datetime, timedelta, timezone
|
6
|
-
from typing import List, Self, Tuple, Union
|
6
|
+
from typing import List, Self, Tuple, Union, TYPE_CHECKING
|
7
7
|
|
8
8
|
import requests
|
9
|
-
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
import typer
|
10
11
|
from looker_sdk.rtl import serialize
|
11
12
|
from looker_sdk.rtl.api_settings import ApiSettings, SettingsConfig
|
12
13
|
from looker_sdk.rtl.auth_session import AuthSession, CryptoHash, OAuthSession
|
@@ -25,7 +26,7 @@ from lkr.logger import logger
|
|
25
26
|
__all__ = ["get_auth", "ApiKeyAuthSession", "DbOAuthSession"]
|
26
27
|
|
27
28
|
|
28
|
-
def get_auth(ctx: typer.Context
|
29
|
+
def get_auth(ctx: Union["typer.Context", LkrCtxObj]) -> Union["SqlLiteAuth", "ApiKeyAuth"]:
|
29
30
|
if isinstance(ctx, LkrCtxObj):
|
30
31
|
lkr_ctx = ctx
|
31
32
|
else:
|
lkr/logger.py
CHANGED
@@ -1,19 +1,28 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
|
+
from lkr.custom_types import LogLevel
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
STRUCT_LOG_AVAILABLE = True
|
6
|
+
RICH_AVAILABLE = True
|
7
|
+
try:
|
8
|
+
import structlog
|
9
|
+
except ModuleNotFoundError:
|
10
|
+
STRUCT_LOG_AVAILABLE = False
|
11
|
+
try:
|
12
|
+
from rich.console import Console
|
13
|
+
from rich.logging import RichHandler
|
14
|
+
from rich.theme import Theme
|
15
|
+
except ModuleNotFoundError:
|
16
|
+
RICH_AVAILABLE = False
|
8
17
|
|
9
|
-
from lkr.custom_types import LogLevel
|
10
18
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
19
|
+
if STRUCT_LOG_AVAILABLE:
|
20
|
+
structlog.configure(
|
21
|
+
processors=[
|
22
|
+
structlog.processors.TimeStamper(fmt="iso"),
|
23
|
+
structlog.processors.JSONRenderer(),
|
24
|
+
]
|
25
|
+
)
|
17
26
|
|
18
27
|
# Define a custom theme for our logging
|
19
28
|
theme = Theme(
|
@@ -24,10 +33,10 @@ theme = Theme(
|
|
24
33
|
"logging.level.error": "bold red",
|
25
34
|
"logging.level.critical": "bold white on red",
|
26
35
|
}
|
27
|
-
)
|
36
|
+
) if RICH_AVAILABLE else None
|
28
37
|
|
29
38
|
# Create a console for logging
|
30
|
-
console = Console(theme=theme)
|
39
|
+
console = Console(theme=theme) if RICH_AVAILABLE else None
|
31
40
|
|
32
41
|
# Configure the logging handler
|
33
42
|
handler = RichHandler(
|
@@ -37,7 +46,7 @@ handler = RichHandler(
|
|
37
46
|
markup=True,
|
38
47
|
rich_tracebacks=True,
|
39
48
|
tracebacks_show_locals=True,
|
40
|
-
)
|
49
|
+
) if RICH_AVAILABLE else None
|
41
50
|
|
42
51
|
# Get log level from environment variable, defaulting to INFO
|
43
52
|
DEFAULT_LOG_LEVEL = "INFO"
|
@@ -50,25 +59,27 @@ logging.basicConfig(
|
|
50
59
|
), # Fallback to INFO if invalid level
|
51
60
|
format="%(message)s",
|
52
61
|
datefmt="[%X]",
|
53
|
-
handlers=[handler],
|
62
|
+
handlers=[handler] if handler else [],
|
54
63
|
)
|
55
64
|
|
56
65
|
# Create a logger for the application
|
57
66
|
logger = logging.getLogger("lkr")
|
58
|
-
structured_logger = structlog.get_logger("lkr.structured")
|
67
|
+
structured_logger = structlog.get_logger("lkr.structured") if STRUCT_LOG_AVAILABLE else None
|
59
68
|
|
60
69
|
|
61
70
|
# Configure the requests_transport logger to only show debug messages when LOG_LEVEL is DEBUG
|
62
|
-
requests_logger = logging.getLogger("looker_sdk.rtl.requests_transport")
|
63
|
-
if log_level != "DEBUG":
|
71
|
+
requests_logger = logging.getLogger("looker_sdk.rtl.requests_transport") if RICH_AVAILABLE else None
|
72
|
+
if log_level != "DEBUG" and requests_logger:
|
64
73
|
requests_logger.setLevel(logging.WARNING)
|
65
74
|
|
66
75
|
|
67
76
|
def set_log_level(level: LogLevel):
|
68
77
|
"""Set the logging level for the application."""
|
69
78
|
logger.setLevel(getattr(logging, level.value))
|
70
|
-
|
79
|
+
if structured_logger:
|
80
|
+
structured_logger.setLevel(getattr(logging, level.value))
|
71
81
|
# Update requests_transport logger level based on the new level
|
72
|
-
requests_logger
|
73
|
-
|
74
|
-
|
82
|
+
if requests_logger:
|
83
|
+
requests_logger.setLevel(
|
84
|
+
logging.DEBUG if level == LogLevel.DEBUG else logging.WARNING
|
85
|
+
)
|
lkr/main.py
CHANGED
@@ -7,9 +7,6 @@ from lkr.auth.main import group as auth_group
|
|
7
7
|
from lkr.classes import LkrCtxObj
|
8
8
|
from lkr.custom_types import LogLevel
|
9
9
|
from lkr.logger import logger
|
10
|
-
from lkr.mcp.main import group as mcp_group
|
11
|
-
from lkr.observability.main import group as observability_group
|
12
|
-
from lkr.tools.main import group as tools_group
|
13
10
|
|
14
11
|
app = typer.Typer(
|
15
12
|
name="lkr",
|
@@ -19,10 +16,32 @@ app = typer.Typer(
|
|
19
16
|
)
|
20
17
|
|
21
18
|
app.add_typer(auth_group, name="auth")
|
22
|
-
app.add_typer(mcp_group, name="mcp")
|
23
|
-
app.add_typer(observability_group, name="observability")
|
24
|
-
app.add_typer(tools_group, name="tools")
|
25
19
|
|
20
|
+
IMPORT_ERROR = None
|
21
|
+
|
22
|
+
def add_optional_typer_group(app, import_path, group_name, extra_message=None):
|
23
|
+
try:
|
24
|
+
module_path, attr = import_path.rsplit(".", 1)
|
25
|
+
mod = __import__(module_path, fromlist=[attr])
|
26
|
+
group = getattr(mod, attr)
|
27
|
+
app.add_typer(group, name=group_name)
|
28
|
+
except ModuleNotFoundError as import_error:
|
29
|
+
@app.command(
|
30
|
+
name=group_name,
|
31
|
+
add_help_option=False,
|
32
|
+
context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
|
33
|
+
)
|
34
|
+
def fallback(import_error=import_error):
|
35
|
+
msg = f"{group_name} tools (dependencies not available, try installing optional dependencies: lkr-dev-cli\\[{group_name}])"
|
36
|
+
if extra_message:
|
37
|
+
msg += f" {extra_message}"
|
38
|
+
logger.error(msg)
|
39
|
+
logger.error(import_error)
|
40
|
+
raise typer.Exit(1)
|
41
|
+
|
42
|
+
add_optional_typer_group(app, "lkr.mcp.main.group", "mcp")
|
43
|
+
add_optional_typer_group(app, "lkr.observability.main.group", "observability")
|
44
|
+
add_optional_typer_group(app, "lkr.tools.main.group", "tools")
|
26
45
|
|
27
46
|
@app.callback()
|
28
47
|
def callback(
|
lkr/observability/main.py
CHANGED
lkr/tools/classes.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
from typing import Literal, Optional, Self, cast
|
2
2
|
|
3
|
-
from fastapi import Request
|
4
3
|
from looker_sdk.sdk.api40.methods import Looker40SDK
|
5
4
|
from looker_sdk.sdk.api40.models import (
|
6
5
|
UserAttributeGroupValue,
|
@@ -46,10 +45,12 @@ class UserAttributeUpdater(BaseModel):
|
|
46
45
|
)
|
47
46
|
return self
|
48
47
|
|
49
|
-
def get_request_authorization_for_value(self,
|
50
|
-
authorization_token =
|
48
|
+
def get_request_authorization_for_value(self, headers: list[tuple[str, str]]):
|
49
|
+
authorization_token = next(
|
50
|
+
(header for header in headers if header[0] == "Authorization"), None
|
51
|
+
)
|
51
52
|
if authorization_token:
|
52
|
-
self.value = authorization_token
|
53
|
+
self.value = authorization_token[1]
|
53
54
|
else:
|
54
55
|
logger.error("No authorization token found")
|
55
56
|
|
lkr/tools/main.py
CHANGED
@@ -11,6 +11,9 @@ __all__ = ["group"]
|
|
11
11
|
|
12
12
|
group = typer.Typer()
|
13
13
|
|
14
|
+
if not logger:
|
15
|
+
raise Exception("Logger is not available")
|
16
|
+
|
14
17
|
|
15
18
|
@group.command()
|
16
19
|
def user_attribute_updater(
|
@@ -22,7 +25,7 @@ def user_attribute_updater(
|
|
22
25
|
@api.post("/identity_token")
|
23
26
|
def identity_token(request: Request, body: UserAttributeUpdater):
|
24
27
|
try:
|
25
|
-
body.get_request_authorization_for_value(request)
|
28
|
+
body.get_request_authorization_for_value(request.headers.items())
|
26
29
|
body.update_user_attribute_value()
|
27
30
|
raw_urls = os.getenv("LOOKER_WHITELISTED_BASE_URLS", "")
|
28
31
|
whitelisted_base_urls = (
|
@@ -1,23 +1,39 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: lkr-dev-cli
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.32
|
4
4
|
Summary: lkr: a command line interface for looker
|
5
5
|
Author: bwebs
|
6
6
|
License-Expression: MIT
|
7
7
|
License-File: LICENSE
|
8
8
|
Requires-Python: >=3.12
|
9
|
-
Requires-Dist: cryptography>=42.0.0
|
10
|
-
Requires-Dist: duckdb>=1.2.2
|
11
|
-
Requires-Dist: fastapi>=0.115.12
|
12
9
|
Requires-Dist: looker-sdk>=25.4.0
|
13
|
-
Requires-Dist: mcp[cli]>=1.9.2
|
14
10
|
Requires-Dist: pydantic>=2.11.4
|
15
11
|
Requires-Dist: pydash>=8.0.5
|
16
|
-
|
17
|
-
Requires-Dist:
|
18
|
-
Requires-Dist:
|
19
|
-
Requires-Dist:
|
20
|
-
Requires-Dist:
|
12
|
+
Provides-Extra: all
|
13
|
+
Requires-Dist: cryptography>=42.0.0; extra == 'all'
|
14
|
+
Requires-Dist: duckdb>=1.2.2; extra == 'all'
|
15
|
+
Requires-Dist: fastapi[standard]>=0.115.12; extra == 'all'
|
16
|
+
Requires-Dist: mcp[cli]>=1.9.2; extra == 'all'
|
17
|
+
Requires-Dist: questionary>=2.1.0; extra == 'all'
|
18
|
+
Requires-Dist: requests>=2.31.0; extra == 'all'
|
19
|
+
Requires-Dist: selenium>=4.32.0; extra == 'all'
|
20
|
+
Requires-Dist: structlog>=25.3.0; extra == 'all'
|
21
|
+
Requires-Dist: typer>=0.15.2; extra == 'all'
|
22
|
+
Provides-Extra: cli
|
23
|
+
Requires-Dist: cryptography>=42.0.0; extra == 'cli'
|
24
|
+
Requires-Dist: questionary>=2.1.0; extra == 'cli'
|
25
|
+
Requires-Dist: requests>=2.31.0; extra == 'cli'
|
26
|
+
Requires-Dist: structlog>=25.3.0; extra == 'cli'
|
27
|
+
Requires-Dist: typer>=0.15.2; extra == 'cli'
|
28
|
+
Provides-Extra: mcp
|
29
|
+
Requires-Dist: duckdb>=1.2.2; extra == 'mcp'
|
30
|
+
Requires-Dist: fastapi[standard]>=0.115.12; extra == 'mcp'
|
31
|
+
Requires-Dist: mcp[cli]>=1.9.2; extra == 'mcp'
|
32
|
+
Provides-Extra: observability
|
33
|
+
Requires-Dist: fastapi[standard]>=0.115.12; extra == 'observability'
|
34
|
+
Requires-Dist: selenium>=4.32.0; extra == 'observability'
|
35
|
+
Provides-Extra: tools
|
36
|
+
Requires-Dist: fastapi[standard]>=0.115.12; extra == 'tools'
|
21
37
|
Description-Content-Type: text/markdown
|
22
38
|
|
23
39
|
# lkr cli
|
@@ -26,9 +42,9 @@ The `lkr` cli is a tool for interacting with Looker. It combines Looker's SDK an
|
|
26
42
|
|
27
43
|
## Usage
|
28
44
|
|
29
|
-
`uv` makes everyone's life easier. Go [install it](https://docs.astral.sh/uv/getting-started/installation/). You can start using `lkr` by running `uv run --with lkr-dev-cli lkr --help`.
|
45
|
+
`uv` makes everyone's life easier. Go [install it](https://docs.astral.sh/uv/getting-started/installation/). You can start using `lkr` by running `uv run --with lkr-dev-cli[all] lkr --help`.
|
30
46
|
|
31
|
-
Alternatively, you can install `lkr` with `pip install lkr-dev-cli` and use commands directly like `lkr <command>`.
|
47
|
+
Alternatively, you can install `lkr` with `pip install lkr-dev-cli[all]` and use commands directly like `lkr <command>`.
|
32
48
|
|
33
49
|
We also have a public docker image that you can use to run `lkr` commands.
|
34
50
|
|
@@ -46,7 +62,7 @@ See the [prerequisites section](#oauth2-prerequisites)
|
|
46
62
|
Login to `lkr`
|
47
63
|
|
48
64
|
```bash
|
49
|
-
uv run --with lkr-dev-cli lkr auth login
|
65
|
+
uv run --with lkr-dev-cli[all] lkr auth login
|
50
66
|
```
|
51
67
|
|
52
68
|
- Select a new instance
|
@@ -59,7 +75,7 @@ You will be redirected to the Looker OAuth authorization page, click Allow. If y
|
|
59
75
|
If everything is successful, you will see `Successfully authenticated!`. Test it with
|
60
76
|
|
61
77
|
```bash
|
62
|
-
uv run --with lkr-dev-cli lkr auth whoami
|
78
|
+
uv run --with lkr-dev-cli[all] lkr auth whoami
|
63
79
|
```
|
64
80
|
|
65
81
|
### Using API Key
|
@@ -67,7 +83,7 @@ uv run --with lkr-dev-cli lkr auth whoami
|
|
67
83
|
If you provide environment variables for `LOOKERSDK_CLIENT_ID`, `LOOKERSDK_CLIENT_SECRET`, and `LOOKERSDK_BASE_URL`, `lkr` will use the API key to authenticate and the commands. We also support command line arguments to pass in the client id, client secret, and base url.
|
68
84
|
|
69
85
|
```bash
|
70
|
-
uv run --with lkr-dev-cli lkr --client-id <your client id> --client-secret <your client secret> --base-url <your instance url> auth whoami
|
86
|
+
uv run --with lkr-dev-cli[all] lkr --client-id <your client id> --client-secret <your client secret> --base-url <your instance url> auth whoami
|
71
87
|
```
|
72
88
|
|
73
89
|
|
@@ -103,7 +119,7 @@ Built into the `lkr` is an MCP server. Right now its tools are based on helping
|
|
103
119
|
"mcpServers": {
|
104
120
|
"lkr-mcp": {
|
105
121
|
"command": "uv",
|
106
|
-
"args": ["run", "--with", "lkr-dev-cli", "lkr", "mcp", "run"]
|
122
|
+
"args": ["run", "--with", "lkr-dev-cli[all]", "lkr", "mcp", "run"]
|
107
123
|
},
|
108
124
|
"lkr-mcp-docker": {
|
109
125
|
"command": "docker",
|
@@ -214,10 +230,13 @@ This can also be used to stress test your Looker environment as it serves an API
|
|
214
230
|
## User Attribute Updater (OIDC Token)
|
215
231
|
|
216
232
|
1. Create a new cloud run using the `lkr-cli` public docker image `us-central1-docker.pkg.dev/lkr-dev-production/lkr-cli/cli:latest`
|
217
|
-
2. Put in the environment variables LOOKERSDK_CLIENT_ID, LOOKERSDK_CLIENT_SECRET, LOOKERSDK_BASE_URL, LOOKER_WHITELISTED_BASE_URLS. The `LOOKER_WHITELISTED_BASE_URLS` would be the same url as the `LOOKERSDK_BASE_URL` if you are only using this for a single Looker instance. For more advanced use cases, you can set the `LOOKER_WHITELISTED_BASE_URLS` to a comma separated list of urls. The body of the request also accepts a `base_url`, `client_id`, and `client_secret` key that will override these settings.
|
218
|
-
3.
|
219
|
-
|
220
|
-
|
233
|
+
2. Put in the environment variables LOOKERSDK_CLIENT_ID, LOOKERSDK_CLIENT_SECRET, LOOKERSDK_BASE_URL, LOOKER_WHITELISTED_BASE_URLS. The `LOOKER_WHITELISTED_BASE_URLS` would be the same url as the `LOOKERSDK_BASE_URL` if you are only using this for a single Looker instance. For more advanced use cases, you can set the `LOOKER_WHITELISTED_BASE_URLS` to a comma separated list of urls. The body of the request also accepts a `base_url`, `client_id`, and `client_secret` key that will override these settings. See example [`gcloud` command](#example-gcloud-command)
|
234
|
+
3. For the command and arguments use:
|
235
|
+
- command: `lkr`
|
236
|
+
- args: `tools` `user-attribute-updater`
|
237
|
+
4. Deploy the cloud run
|
238
|
+
5. Retrieve the URL of the cloud run
|
239
|
+
6. Create the user attribute
|
221
240
|
- Name: cloud_run_access_token
|
222
241
|
- Data Type: String
|
223
242
|
- User Access: None
|
@@ -250,6 +269,23 @@ This can also be used to stress test your Looker environment as it serves an API
|
|
250
269
|
9. Check the logs of the cloud run to see if there was a 200 response
|
251
270
|
|
252
271
|
|
272
|
+
### Example `gcloud` command
|
273
|
+
```bash
|
274
|
+
export REGION=<your region>
|
275
|
+
export PROJECT=<your project id>
|
276
|
+
|
277
|
+
gcloud run deploy lkr-access-token-updater \
|
278
|
+
--image us-central1-docker.pkg.dev/lkr-dev-production/lkr-cli/cli:latest \
|
279
|
+
--command lkr \
|
280
|
+
--args tools,user-attribute-updater \
|
281
|
+
--platform managed \
|
282
|
+
--region $REGION \
|
283
|
+
--project $PROJECT \
|
284
|
+
--cpu 1 \
|
285
|
+
--memory 2Gi \
|
286
|
+
--set-env-vars LOOKERSDK_CLIENT_ID=<your client id>,LOOKERSDK_CLIENT_SECRET=<your client secret>,LOOKERSDK_BASE_URL=<your instance url>,LOOKER_WHITELISTED_BASE_URLS=<your instance url>
|
287
|
+
```
|
288
|
+
|
253
289
|
## UserAttributeUpdater `lkr-dev-cli`
|
254
290
|
|
255
291
|
Exported from the `lkr-dev-cli` package is the `UserAttributeUpdater` pydantic class. This class has all the necessary logic to update a user attribute value.
|
@@ -301,12 +337,12 @@ from lkr import UserAttributeUpdater
|
|
301
337
|
def request_authorization(request: Request):
|
302
338
|
body = await request.json()
|
303
339
|
updater = UserAttributeUpdater.model_validate(body)
|
304
|
-
updater.get_request_authorization_for_value(request)
|
340
|
+
updater.get_request_authorization_for_value(request.headers.items())
|
305
341
|
updater.update_user_attribute_value()
|
306
342
|
|
307
343
|
@app.post("/as_body")
|
308
344
|
def as_body(request: Request, body: UserAttributeUpdater):
|
309
|
-
body.get_request_authorization_for_value(request)
|
345
|
+
body.get_request_authorization_for_value(request.headers.items())
|
310
346
|
body.update_user_attribute_value()
|
311
347
|
|
312
348
|
@app.post("/assigning_value")
|
@@ -317,4 +353,53 @@ def assigning_value(request: Request):
|
|
317
353
|
)
|
318
354
|
updater.value = request.headers.get("my_custom_header")
|
319
355
|
updater.update_user_attribute_value()
|
356
|
+
|
357
|
+
@app.delete("/:user_attribute_name/:email")
|
358
|
+
def delete_user_attribute(user_attribute_name: str, email: str):
|
359
|
+
updater = UserAttributeUpdater(
|
360
|
+
user_attribute=user_attribute_name,
|
361
|
+
update_type="user",
|
362
|
+
email=email,
|
363
|
+
)
|
364
|
+
updater.delete_user_attribute_value()
|
365
|
+
|
366
|
+
## Optional Dependencies
|
367
|
+
|
368
|
+
The `lkr` CLI supports optional dependencies that enable additional functionality. You can install these individually or all at once.
|
369
|
+
|
370
|
+
### Available Extras
|
371
|
+
|
372
|
+
- **`mcp`**: Enables the MCP (Model Context Protocol) server functionality and `lkr mcp` commands
|
373
|
+
- **`observability`**: Enables the observability embed monitoring features and `lkr observability` commands
|
374
|
+
- **`tools`**: Enables the user attribute updater functionality and `lkr tools` commands
|
375
|
+
|
376
|
+
### Installing Optional Dependencies
|
377
|
+
|
378
|
+
**Install all optional dependencies:**
|
379
|
+
```bash
|
380
|
+
uv sync --extra all
|
381
|
+
```
|
382
|
+
|
383
|
+
**Install specific extras:**
|
384
|
+
```bash
|
385
|
+
# Install MCP functionality
|
386
|
+
uv sync --extra mcp
|
387
|
+
|
388
|
+
# Install observability features
|
389
|
+
uv sync --extra embed-observability
|
390
|
+
|
391
|
+
# Install user attribute updater
|
392
|
+
uv sync --extra user-attribute-updater
|
393
|
+
|
394
|
+
# Install multiple extras
|
395
|
+
uv sync --extra mcp --extra embed-observability
|
396
|
+
```
|
397
|
+
|
398
|
+
**Using pip:**
|
399
|
+
```bash
|
400
|
+
# Install all optional dependencies
|
401
|
+
pip install lkr-dev-cli[all]
|
402
|
+
|
403
|
+
# Install specific extras
|
404
|
+
pip install lkr-dev-cli[mcp,embed-observability,user-attribute-updater]
|
320
405
|
```
|
@@ -1,25 +1,25 @@
|
|
1
1
|
lkr/__init__.py,sha256=IooxWuZJ0XeK6dTc0PTRy3pVhofSoepOVJHBMuBWByI,87
|
2
|
-
lkr/auth_service.py,sha256=
|
2
|
+
lkr/auth_service.py,sha256=svpwzSHHxvWKcSGq0wi9xUAnlhc1Uxo8myWfi3PBvbY,19932
|
3
3
|
lkr/classes.py,sha256=f2TJOXFta0s8LJLEXOqPdWPLg-EIIntUSDS6gDOon7M,1163
|
4
4
|
lkr/constants.py,sha256=DdCfsV6q8wgs2iHpIQeb6oDP_2XejusEHyPvCbaM3yY,108
|
5
5
|
lkr/custom_types.py,sha256=feJ-W2U61PJTiotMLuZJqxrotA53er95kO1O30mooy4,323
|
6
6
|
lkr/exceptions.py,sha256=M_aR4YaCZtY4wyxhcoqJCVkxVu9z3Wwo5KgSDyOoEnI,210
|
7
|
-
lkr/logger.py,sha256=
|
8
|
-
lkr/main.py,sha256=
|
7
|
+
lkr/logger.py,sha256=YIcQR2xDhTXXrSDkFquHW3s5P8BPyiJsWqLucD6X3E8,2507
|
8
|
+
lkr/main.py,sha256=d6MEayMLq1j0qJjPUA3chOAxOm6FzF46Pzq6_f1X4oM,3295
|
9
9
|
lkr/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
lkr/auth/main.py,sha256=
|
10
|
+
lkr/auth/main.py,sha256=wgU4fXelETwEDxfMSjRLhqqJN4JljqZJIcI8F8C8krw,7802
|
11
11
|
lkr/auth/oauth.py,sha256=n2yAcccdBZaloGVtFRTwCPBfh1cvVYNbXLsFCxmWc5M,7207
|
12
12
|
lkr/mcp/classes.py,sha256=dqtZxivftufQpQNXmlzzhx1lw3nN7DwTvFc6pZUF_bA,2992
|
13
13
|
lkr/mcp/main.py,sha256=JRMfdNk5_tqxWz8caqKBmcxP2ieNXAE4-1HjEgC4jl4,21693
|
14
14
|
lkr/mcp/utils.py,sha256=lXkXmoD-a7WkJYI4qqzh-n1wE4T5YajOkALZtjJxYWg,1378
|
15
15
|
lkr/observability/classes.py,sha256=LgGuUnY-J1csPrlAKnw4PPOqOfbvaOx2cxENlQgJYcE,5816
|
16
16
|
lkr/observability/embed_container.html,sha256=IcDG-QVsYYNGQGrkDrx9OMZ2Pmo4C8oAjRHddFQ7Tlw,2939
|
17
|
-
lkr/observability/main.py,sha256=
|
17
|
+
lkr/observability/main.py,sha256=Xbe7Bmmyl8PduK51YDZropsteuJmTqgEKaEsqG3Ft10,9628
|
18
18
|
lkr/observability/utils.py,sha256=UpaBrp_ufaXLoz4p3xG3K6lHKBpP9wBhvP8rDmeGoWg,2148
|
19
|
-
lkr/tools/classes.py,sha256=
|
20
|
-
lkr/tools/main.py,sha256=
|
21
|
-
lkr_dev_cli-0.0.
|
22
|
-
lkr_dev_cli-0.0.
|
23
|
-
lkr_dev_cli-0.0.
|
24
|
-
lkr_dev_cli-0.0.
|
25
|
-
lkr_dev_cli-0.0.
|
19
|
+
lkr/tools/classes.py,sha256=ZyRRCQjjwV4WVWGmKlTfXiLiOGUf67XgrboYhOLuLts,7508
|
20
|
+
lkr/tools/main.py,sha256=l02twcunEiW13dTXcXqoodjlh6S0IAXowEAcs0sDIIw,2746
|
21
|
+
lkr_dev_cli-0.0.32.dist-info/METADATA,sha256=z-IguQt3xAWHJN3MVN2lgEIP8xVmPo_9xmm-Xm1JWJI,18411
|
22
|
+
lkr_dev_cli-0.0.32.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
23
|
+
lkr_dev_cli-0.0.32.dist-info/entry_points.txt,sha256=nn2sFMGDpwUVE61ZUpbDPnQZkW7Gc08nV-tyLGo8q34,37
|
24
|
+
lkr_dev_cli-0.0.32.dist-info/licenses/LICENSE,sha256=hKnCOORW1JRE_M2vStz8dblS5u1iR-2VpqS9xagKNa0,1063
|
25
|
+
lkr_dev_cli-0.0.32.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|