lkr-dev-cli 0.0.24__tar.gz → 0.0.26__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 (36) hide show
  1. lkr_dev_cli-0.0.26/.vscode/launch.json +41 -0
  2. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/PKG-INFO +13 -6
  3. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/README.md +11 -4
  4. lkr_dev_cli-0.0.26/lkr/__init__.py +3 -0
  5. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/lkr/logger.py +8 -0
  6. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/lkr/main.py +2 -0
  7. lkr_dev_cli-0.0.26/lkr/mcp/classes.py +99 -0
  8. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/lkr/mcp/main.py +132 -193
  9. lkr_dev_cli-0.0.26/lkr/mcp/utils.py +55 -0
  10. lkr_dev_cli-0.0.26/lkr/tools/classes.py +206 -0
  11. lkr_dev_cli-0.0.26/lkr/tools/main.py +55 -0
  12. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/pyproject.toml +2 -2
  13. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/uv.lock +17 -86
  14. lkr_dev_cli-0.0.24/.vscode/launch.json +0 -19
  15. lkr_dev_cli-0.0.24/lkr/auth/__init__.py +0 -0
  16. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/.github/workflows/release.yml +0 -0
  17. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/.gitignore +0 -0
  18. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/.python-version +0 -0
  19. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/.vscode/settings.json +0 -0
  20. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/Dockerfile +0 -0
  21. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/LICENSE +0 -0
  22. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/Makefile +0 -0
  23. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/cloudbuild.yaml +0 -0
  24. {lkr_dev_cli-0.0.24/lkr → lkr_dev_cli-0.0.26/lkr/auth}/__init__.py +0 -0
  25. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/lkr/auth/main.py +0 -0
  26. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/lkr/auth/oauth.py +0 -0
  27. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/lkr/auth_service.py +0 -0
  28. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/lkr/classes.py +0 -0
  29. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/lkr/constants.py +0 -0
  30. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/lkr/custom_types.py +0 -0
  31. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/lkr/exceptions.py +0 -0
  32. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/lkr/observability/classes.py +0 -0
  33. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/lkr/observability/embed_container.html +0 -0
  34. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/lkr/observability/main.py +0 -0
  35. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/lkr/observability/utils.py +0 -0
  36. {lkr_dev_cli-0.0.24 → lkr_dev_cli-0.0.26}/lkr.md +0 -0
@@ -0,0 +1,41 @@
1
+ {
2
+ // Use IntelliSense to learn about possible attributes.
3
+ // Hover to view descriptions of existing attributes.
4
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5
+ "version": "0.2.0",
6
+ "configurations": [
7
+ {
8
+ "name": "lkr auth login",
9
+ "type": "debugpy",
10
+ "request": "launch",
11
+ "program": "${workspaceFolder}/.venv/bin/lkr",
12
+ "args": ["auth", "login"],
13
+ "justMyCode": false,
14
+ "env": {
15
+ "PYTHONPATH": "${workspaceFolder}"
16
+ }
17
+ },
18
+ {
19
+ "name": "lkr mcp run",
20
+ "type": "debugpy",
21
+ "request": "launch",
22
+ "program": "${workspaceFolder}/.venv/bin/lkr",
23
+ "args": ["mcp", "run", "--debug"],
24
+ "justMyCode": false,
25
+ "env": {
26
+ "PYTHONPATH": "${workspaceFolder}"
27
+ }
28
+ },
29
+ {
30
+ "name": "lkr tools access token updater",
31
+ "type": "debugpy",
32
+ "request": "launch",
33
+ "program": "${workspaceFolder}/.venv/bin/lkr",
34
+ "args": ["tools", "access-token-updater"],
35
+ "justMyCode": false,
36
+ "env": {
37
+ "PYTHONPATH": "${workspaceFolder}"
38
+ }
39
+ }
40
+ ]
41
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lkr-dev-cli
3
- Version: 0.0.24
3
+ Version: 0.0.26
4
4
  Summary: lkr: a command line interface for looker
5
5
  Author: bwebs
6
6
  License-Expression: MIT
@@ -9,8 +9,8 @@ Requires-Python: >=3.12
9
9
  Requires-Dist: cryptography>=42.0.0
10
10
  Requires-Dist: duckdb>=1.2.2
11
11
  Requires-Dist: fastapi>=0.115.12
12
- Requires-Dist: fastmcp>=2.3.5
13
12
  Requires-Dist: looker-sdk>=25.4.0
13
+ Requires-Dist: mcp[cli]>=1.9.2
14
14
  Requires-Dist: pydantic>=2.11.4
15
15
  Requires-Dist: pydash>=8.0.5
16
16
  Requires-Dist: questionary>=2.1.0
@@ -178,26 +178,33 @@ For example:
178
178
 
179
179
  ### Cloud Run + GCP Health Check example
180
180
 
181
- One of the simplest ways to launch the health check is the `lkr-cli` public docker image, Cloud Run, and the GCP health check service. Here's an example; make sure to change your region, models, user_attributes, and external_user_id.
181
+ One of the simplest ways to launch the health check is the `lkr-cli` public docker image, Cloud Run, and the GCP health check service. Here's an example; make sure to change your region and project. HEALTH_URL is an example of how to structure the query parameters for the health check.
182
182
 
183
183
  ```bash
184
184
  export REGION=<your region>
185
185
  export PROJECT=<your project id>
186
186
 
187
- export HEALTH_URL="/health?dashboard_id=1&external_user_id=embed-user-abc&models=thelook&user_attributes={\"store_id\":\"1\"}"
187
+ export HEALTH_URL="/health?dashboard_id=1&external_user_id=observability-embed-user&models=thelook&user_attributes={\"store_id\":\"1\"}"
188
188
 
189
189
  gcloud run deploy lkr-observability \
190
190
  --image us-central1-docker.pkg.dev/lkr-dev-production/lkr-cli/cli:latest \
191
191
  --command lkr \
192
192
  --args observability,embed \
193
193
  --platform managed \
194
- --region $REGION \
194
+ --region $REGION \
195
195
  --project $PROJECT \
196
196
  --cpu 2 \
197
197
  --memory 4Gi \
198
- --liveness-probe httpGet.path=$HEALTH_URL,timeoutSeconds=20,periodSeconds=20 \
199
198
  --set-env-vars LOOKERSDK_CLIENT_ID=<your client id>,LOOKERSDK_CLIENT_SECRET=<your client secret>,LOOKERSDK_BASE_URL=<your instance url>
200
199
 
200
+ gcloud monitoring uptime create lkr-observability-health-check \
201
+ --protocol https \
202
+ --project $PROJECT \
203
+ --resource-type="cloud-run-revision" \
204
+ --resource-labels="project_id=${PROJECT},service_name=lkr-observability,location=${REGION}" \
205
+ --path="${HEALTH_URL}" \
206
+ --period="15" \
207
+ --timeout="60"
201
208
 
202
209
  ```
203
210
 
@@ -156,26 +156,33 @@ For example:
156
156
 
157
157
  ### Cloud Run + GCP Health Check example
158
158
 
159
- One of the simplest ways to launch the health check is the `lkr-cli` public docker image, Cloud Run, and the GCP health check service. Here's an example; make sure to change your region, models, user_attributes, and external_user_id.
159
+ One of the simplest ways to launch the health check is the `lkr-cli` public docker image, Cloud Run, and the GCP health check service. Here's an example; make sure to change your region and project. HEALTH_URL is an example of how to structure the query parameters for the health check.
160
160
 
161
161
  ```bash
162
162
  export REGION=<your region>
163
163
  export PROJECT=<your project id>
164
164
 
165
- export HEALTH_URL="/health?dashboard_id=1&external_user_id=embed-user-abc&models=thelook&user_attributes={\"store_id\":\"1\"}"
165
+ export HEALTH_URL="/health?dashboard_id=1&external_user_id=observability-embed-user&models=thelook&user_attributes={\"store_id\":\"1\"}"
166
166
 
167
167
  gcloud run deploy lkr-observability \
168
168
  --image us-central1-docker.pkg.dev/lkr-dev-production/lkr-cli/cli:latest \
169
169
  --command lkr \
170
170
  --args observability,embed \
171
171
  --platform managed \
172
- --region $REGION \
172
+ --region $REGION \
173
173
  --project $PROJECT \
174
174
  --cpu 2 \
175
175
  --memory 4Gi \
176
- --liveness-probe httpGet.path=$HEALTH_URL,timeoutSeconds=20,periodSeconds=20 \
177
176
  --set-env-vars LOOKERSDK_CLIENT_ID=<your client id>,LOOKERSDK_CLIENT_SECRET=<your client secret>,LOOKERSDK_BASE_URL=<your instance url>
178
177
 
178
+ gcloud monitoring uptime create lkr-observability-health-check \
179
+ --protocol https \
180
+ --project $PROJECT \
181
+ --resource-type="cloud-run-revision" \
182
+ --resource-labels="project_id=${PROJECT},service_name=lkr-observability,location=${REGION}" \
183
+ --path="${HEALTH_URL}" \
184
+ --period="15" \
185
+ --timeout="60"
179
186
 
180
187
  ```
181
188
 
@@ -0,0 +1,3 @@
1
+ from lkr.tools.classes import UserAttributeUpdater
2
+
3
+ __all__ = ["UserAttributeUpdater"]
@@ -8,6 +8,13 @@ from rich.theme import Theme
8
8
 
9
9
  from lkr.custom_types import LogLevel
10
10
 
11
+ structlog.configure(
12
+ processors=[
13
+ structlog.processors.TimeStamper(fmt="iso"),
14
+ structlog.processors.JSONRenderer(),
15
+ ]
16
+ )
17
+
11
18
  # Define a custom theme for our logging
12
19
  theme = Theme(
13
20
  {
@@ -50,6 +57,7 @@ logging.basicConfig(
50
57
  logger = logging.getLogger("lkr")
51
58
  structured_logger = structlog.get_logger("lkr.structured")
52
59
 
60
+
53
61
  # Configure the requests_transport logger to only show debug messages when LOG_LEVEL is DEBUG
54
62
  requests_logger = logging.getLogger("looker_sdk.rtl.requests_transport")
55
63
  if log_level != "DEBUG":
@@ -9,6 +9,7 @@ from lkr.custom_types import LogLevel
9
9
  from lkr.logger import logger
10
10
  from lkr.mcp.main import group as mcp_group
11
11
  from lkr.observability.main import group as observability_group
12
+ from lkr.tools.main import group as tools_group
12
13
 
13
14
  app = typer.Typer(
14
15
  name="lkr", help="LookML Repository CLI", add_completion=True, no_args_is_help=True
@@ -17,6 +18,7 @@ app = typer.Typer(
17
18
  app.add_typer(auth_group, name="auth")
18
19
  app.add_typer(mcp_group, name="mcp")
19
20
  app.add_typer(observability_group, name="observability")
21
+ app.add_typer(tools_group, name="tools")
20
22
 
21
23
 
22
24
  @app.callback()
@@ -0,0 +1,99 @@
1
+ # server.py
2
+ from datetime import datetime
3
+ from typing import Annotated, Any, List
4
+
5
+ import duckdb
6
+ from pydantic import BaseModel, Field, computed_field
7
+
8
+ from lkr.mcp.utils import get_database_search_file
9
+ from lkr.observability.classes import now
10
+
11
+
12
+ class SpectaclesResponse(BaseModel):
13
+ success: bool
14
+ result: Any | None = None
15
+ error: str | None = None
16
+ sql: str | None = None
17
+ share_url: str | None = None
18
+
19
+
20
+ class SpectaclesRequest(BaseModel):
21
+ model: Annotated[
22
+ str,
23
+ Field(
24
+ description="the model to run a test query against, you can find this by the filenames in the repository, they will end with .model.lkml. You should not pass in the .model.lkml extension.",
25
+ default="",
26
+ ),
27
+ ]
28
+ explore: Annotated[
29
+ str,
30
+ Field(
31
+ description="the explore to run a test query against, you can find this by finding explore: <name> {} in any file in the repository",
32
+ default="",
33
+ ),
34
+ ]
35
+ fields: Annotated[
36
+ List[str],
37
+ Field(
38
+ description="this should be the list of fields you want to return from the test query. If the user does not provide them, use all that have changed in your current context",
39
+ default=[],
40
+ ),
41
+ ]
42
+
43
+
44
+ class Connection(BaseModel):
45
+ connection: str
46
+ updated_at: datetime = Field(default_factory=now)
47
+
48
+ @computed_field(return_type=str)
49
+ @property
50
+ def fully_qualified_name(self) -> str:
51
+ return self.connection
52
+
53
+
54
+ class Database(Connection):
55
+ database: str
56
+
57
+ @computed_field(return_type=str)
58
+ @property
59
+ def fully_qualified_name(self) -> str:
60
+ return f"{self.connection}.{self.database}"
61
+
62
+
63
+ class Schema(Database):
64
+ database_schema_name: str
65
+
66
+ @computed_field(return_type=str)
67
+ @property
68
+ def fully_qualified_name(self) -> str:
69
+ return f"{self.connection}.{self.database}.{self.database_schema_name}"
70
+
71
+
72
+ class Table(Schema):
73
+ database_table_name: str
74
+
75
+ @computed_field(return_type=str)
76
+ @property
77
+ def fully_qualified_name(self) -> str:
78
+ return f"{self.connection}.{self.database}.{self.database_schema_name}.{self.database_table_name}"
79
+
80
+
81
+ class Row(Table):
82
+ database_column_name: str
83
+ data_type_database: str
84
+ data_type_looker: str
85
+
86
+ @computed_field(return_type=str)
87
+ @property
88
+ def fully_qualified_name(self) -> str:
89
+ return f"{self.connection}.{self.database}.{self.database_schema_name}.{self.database_table_name}.{self.database_column_name}"
90
+
91
+ def append(self, base_url: str) -> None:
92
+ with open(get_database_search_file(base_url), "a") as f:
93
+ f.write(self.model_dump_json() + "\n")
94
+
95
+ def exists(self, conn: duckdb.DuckDBPyConnection, *, base_url: str) -> bool:
96
+ columns = conn.execute(
97
+ f"SELECT * FROM read_json_auto('{get_database_search_file(base_url)}') WHERE fully_qualified_name = '{self.fully_qualified_name}'"
98
+ ).fetchall()
99
+ return len(columns) > 0