feat: funciones Python para API Metabase
Añade módulo Python con funciones para la API de Metabase en dominio infra. Incluye cliente HTTP, auth, y CRUD de cards, dashboards y users. Proyecto gestionado con uv (pyproject.toml).
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
from .client import MetabaseClient
|
||||
from .users import metabase_list_users, metabase_get_user, metabase_create_user, metabase_update_user, metabase_deactivate_user
|
||||
from .cards import metabase_list_cards, metabase_get_card, metabase_create_card, metabase_update_card, metabase_delete_card, metabase_execute_card, metabase_execute_query
|
||||
from .dashboards import metabase_list_dashboards, metabase_get_dashboard, metabase_create_dashboard, metabase_update_dashboard, metabase_delete_dashboard
|
||||
|
||||
__all__ = [
|
||||
"MetabaseClient",
|
||||
"metabase_list_users", "metabase_get_user", "metabase_create_user", "metabase_update_user", "metabase_deactivate_user",
|
||||
"metabase_list_cards", "metabase_get_card", "metabase_create_card", "metabase_update_card", "metabase_delete_card", "metabase_execute_card", "metabase_execute_query",
|
||||
"metabase_list_dashboards", "metabase_get_dashboard", "metabase_create_dashboard", "metabase_update_dashboard", "metabase_delete_dashboard",
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,227 @@
|
||||
"""CRUD de cards/preguntas de Metabase y ejecucion de queries."""
|
||||
|
||||
from .client import MetabaseClient
|
||||
|
||||
|
||||
def metabase_list_cards(
|
||||
client: MetabaseClient,
|
||||
filter: str = "",
|
||||
model_id: int = 0,
|
||||
) -> list[dict]:
|
||||
"""Lista preguntas/cards de Metabase con filtro opcional.
|
||||
|
||||
Endpoint: GET /api/card. No tiene paginacion offset/limit.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
filter: "all", "mine", "fav", "archived", "recent", "popular",
|
||||
"database", "table". Vacio = todas.
|
||||
model_id: ID de database/tabla. Solo aplica con filter "database" o "table".
|
||||
|
||||
Returns:
|
||||
Lista de dicts, cada uno con: id, name, description, display,
|
||||
collection_id, database_id, creator_id, archived, dataset_query.
|
||||
|
||||
Example:
|
||||
>>> cards = metabase_list_cards(client, filter="mine")
|
||||
>>> for c in cards:
|
||||
... print(c["id"], c["name"], c["display"])
|
||||
"""
|
||||
params = {}
|
||||
if filter:
|
||||
params["f"] = filter
|
||||
if model_id > 0:
|
||||
params["model_id"] = model_id
|
||||
return client.request("GET", "/api/card", params=params)
|
||||
|
||||
|
||||
def metabase_get_card(client: MetabaseClient, card_id: int) -> dict:
|
||||
"""Obtiene los detalles completos de una card/pregunta.
|
||||
|
||||
Endpoint: GET /api/card/:id.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
card_id: ID de la card.
|
||||
|
||||
Returns:
|
||||
Dict con: id, name, description, display, dataset_query,
|
||||
visualization_settings, collection_id, database_id, archived,
|
||||
creator, created_at, updated_at.
|
||||
|
||||
Example:
|
||||
>>> card = metabase_get_card(client, 42)
|
||||
>>> print(card["name"], card["display"])
|
||||
>>> print(card["dataset_query"]["native"]["query"]) # SQL
|
||||
"""
|
||||
return client.request("GET", f"/api/card/{card_id}")
|
||||
|
||||
|
||||
def metabase_create_card(
|
||||
client: MetabaseClient,
|
||||
name: str,
|
||||
dataset_query: dict,
|
||||
display: str = "table",
|
||||
collection_id: int = 0,
|
||||
description: str = "",
|
||||
) -> dict:
|
||||
"""Crea una nueva card/pregunta en Metabase.
|
||||
|
||||
Endpoint: POST /api/card.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
name: Nombre de la pregunta.
|
||||
dataset_query: Query de la card. Estructura:
|
||||
SQL nativo: {"database": 1, "type": "native", "native": {"query": "SELECT ..."}}
|
||||
MBQL: {"database": 1, "type": "query", "query": {"source-table": 4, ...}}
|
||||
display: Tipo de visualizacion: "table", "bar", "line", "pie", "scalar",
|
||||
"area", "row", "combo", "funnel", "scatter", "waterfall", etc.
|
||||
collection_id: ID de coleccion destino. 0 = root.
|
||||
description: Descripcion opcional.
|
||||
|
||||
Returns:
|
||||
Dict con la card creada.
|
||||
|
||||
Example:
|
||||
>>> card = metabase_create_card(client, "Revenue by Month", {
|
||||
... "database": 1,
|
||||
... "type": "native",
|
||||
... "native": {"query": "SELECT date_trunc('month', created_at), SUM(total) FROM orders GROUP BY 1"},
|
||||
... }, display="line", description="Monthly revenue trend")
|
||||
"""
|
||||
body: dict = {
|
||||
"name": name,
|
||||
"dataset_query": dataset_query,
|
||||
"display": display,
|
||||
"visualization_settings": {},
|
||||
}
|
||||
if collection_id > 0:
|
||||
body["collection_id"] = collection_id
|
||||
if description:
|
||||
body["description"] = description
|
||||
return client.request("POST", "/api/card", json=body)
|
||||
|
||||
|
||||
def metabase_update_card(client: MetabaseClient, card_id: int, **fields) -> dict:
|
||||
"""Actualiza campos de una card/pregunta en Metabase.
|
||||
|
||||
Endpoint: PUT /api/card/:id. Solo se modifican los campos pasados.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
card_id: ID de la card.
|
||||
**fields: Campos a actualizar. Validos:
|
||||
name (str), description (str), display (str),
|
||||
dataset_query (dict), visualization_settings (dict),
|
||||
collection_id (int), archived (bool),
|
||||
enable_embedding (bool), embedding_params (dict).
|
||||
|
||||
Returns:
|
||||
Dict con la card actualizada.
|
||||
|
||||
Example:
|
||||
>>> metabase_update_card(client, 42, name="Updated Name", archived=True)
|
||||
>>> metabase_update_card(client, 42, dataset_query={
|
||||
... "database": 1, "type": "native",
|
||||
... "native": {"query": "SELECT * FROM users LIMIT 100"},
|
||||
... })
|
||||
"""
|
||||
return client.request("PUT", f"/api/card/{card_id}", json=fields)
|
||||
|
||||
|
||||
def metabase_delete_card(client: MetabaseClient, card_id: int) -> None:
|
||||
"""Elimina permanentemente una card/pregunta.
|
||||
|
||||
Endpoint: DELETE /api/card/:id. IRREVERSIBLE.
|
||||
Para soft-delete preferir: metabase_update_card(client, card_id, archived=True)
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
card_id: ID de la card a eliminar.
|
||||
|
||||
Example:
|
||||
>>> metabase_delete_card(client, 42)
|
||||
>>> # Preferir soft-delete: metabase_update_card(client, 42, archived=True)
|
||||
"""
|
||||
client.request("DELETE", f"/api/card/{card_id}")
|
||||
|
||||
|
||||
def metabase_execute_card(
|
||||
client: MetabaseClient,
|
||||
card_id: int,
|
||||
parameters: list[dict] | None = None,
|
||||
) -> dict:
|
||||
"""Ejecuta la query de una card/pregunta guardada.
|
||||
|
||||
Endpoint: POST /api/card/:id/query.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
card_id: ID de la card a ejecutar.
|
||||
parameters: Parametros para queries parametrizadas. Cada parametro:
|
||||
{"type": "category", "target": ["variable", ["template-tag", "tag"]], "value": "val"}
|
||||
|
||||
Returns:
|
||||
Dict con resultados:
|
||||
- status: "completed" o "failed"
|
||||
- row_count: numero de filas
|
||||
- running_time: tiempo en ms
|
||||
- data.columns: nombres de columnas
|
||||
- data.rows: filas de datos (lista de listas)
|
||||
- data.cols: metadata de columnas
|
||||
- data.native_form.query: SQL ejecutado
|
||||
|
||||
Example:
|
||||
>>> result = metabase_execute_card(client, 42)
|
||||
>>> for row in result["data"]["rows"]:
|
||||
... print(row)
|
||||
>>> # Con parametros:
|
||||
>>> result = metabase_execute_card(client, 42, parameters=[
|
||||
... {"type": "category", "target": ["variable", ["template-tag", "status"]], "value": "active"},
|
||||
... ])
|
||||
"""
|
||||
body = {}
|
||||
if parameters:
|
||||
body["parameters"] = parameters
|
||||
return client.request("POST", f"/api/card/{card_id}/query", json=body or None)
|
||||
|
||||
|
||||
def metabase_execute_query(
|
||||
client: MetabaseClient,
|
||||
database_id: int,
|
||||
sql: str,
|
||||
max_results: int = 0,
|
||||
) -> dict:
|
||||
"""Ejecuta una query SQL ad-hoc sin guardarla como card.
|
||||
|
||||
Endpoint: POST /api/dataset. Util para exploracion rapida y consultas
|
||||
que no necesitan persistirse.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
database_id: ID de la database en Metabase.
|
||||
sql: Query SQL a ejecutar.
|
||||
max_results: Limite de filas. 0 = default 2000.
|
||||
|
||||
Returns:
|
||||
Dict con misma estructura que metabase_execute_card:
|
||||
data.columns, data.rows, row_count, running_time, status.
|
||||
|
||||
Example:
|
||||
>>> result = metabase_execute_query(client, 1, "SELECT * FROM users LIMIT 10")
|
||||
>>> print(f"{result['row_count']} filas en {result['running_time']}ms")
|
||||
>>> for row in result["data"]["rows"]:
|
||||
... print(row)
|
||||
"""
|
||||
body: dict = {
|
||||
"database": database_id,
|
||||
"type": "native",
|
||||
"native": {"query": sql},
|
||||
}
|
||||
if max_results > 0:
|
||||
body["constraints"] = {
|
||||
"max-results": max_results,
|
||||
"max-results-bare-rows": max_results,
|
||||
}
|
||||
return client.request("POST", "/api/dataset", json=body)
|
||||
@@ -0,0 +1,87 @@
|
||||
"""Cliente base para la API REST de Metabase."""
|
||||
|
||||
import httpx
|
||||
|
||||
|
||||
class MetabaseClient:
|
||||
"""Cliente HTTP para una instancia Metabase.
|
||||
|
||||
Attributes:
|
||||
base_url: URL base sin trailing slash (ej: "http://localhost:3000").
|
||||
token: Session token o API key.
|
||||
_http: Cliente httpx reutilizable con headers de auth.
|
||||
"""
|
||||
|
||||
def __init__(self, base_url: str, token: str) -> None:
|
||||
self.base_url = base_url.rstrip("/")
|
||||
self.token = token
|
||||
self._http = httpx.Client(
|
||||
base_url=self.base_url,
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"X-Metabase-Session": token,
|
||||
},
|
||||
timeout=30.0,
|
||||
)
|
||||
|
||||
def request(self, method: str, path: str, **kwargs) -> dict | list | None:
|
||||
"""Ejecuta una peticion HTTP contra la API de Metabase.
|
||||
|
||||
Args:
|
||||
method: HTTP method (GET, POST, PUT, DELETE).
|
||||
path: Ruta relativa (ej: "/api/user").
|
||||
**kwargs: Argumentos extra para httpx (json, params, etc.).
|
||||
|
||||
Returns:
|
||||
Respuesta deserializada como dict/list, o None si el body esta vacio.
|
||||
|
||||
Raises:
|
||||
httpx.HTTPStatusError: Si el status code no es 2xx.
|
||||
"""
|
||||
resp = self._http.request(method, path, **kwargs)
|
||||
resp.raise_for_status()
|
||||
if not resp.content:
|
||||
return None
|
||||
return resp.json()
|
||||
|
||||
def close(self) -> None:
|
||||
"""Cierra el cliente HTTP."""
|
||||
self._http.close()
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
self.close()
|
||||
|
||||
|
||||
def metabase_auth(base_url: str, email: str, password: str) -> MetabaseClient:
|
||||
"""Autentica contra Metabase con email y password.
|
||||
|
||||
Crea una sesion via POST /api/session y retorna un MetabaseClient
|
||||
con el session token listo para usar. El token expira en 14 dias
|
||||
por defecto (configurable con MAX_SESSION_AGE en Metabase).
|
||||
|
||||
Args:
|
||||
base_url: URL base de la instancia (ej: "http://localhost:3000").
|
||||
email: Email del usuario Metabase.
|
||||
password: Password del usuario.
|
||||
|
||||
Returns:
|
||||
MetabaseClient autenticado con session token.
|
||||
|
||||
Raises:
|
||||
httpx.HTTPStatusError: Si las credenciales son invalidas (401)
|
||||
o hay rate limiting.
|
||||
|
||||
Example:
|
||||
>>> client = metabase_auth("http://localhost:3000", "admin@example.com", "pass")
|
||||
>>> # client listo para usar con todas las funciones CRUD
|
||||
"""
|
||||
resp = httpx.post(
|
||||
f"{base_url.rstrip('/')}/api/session",
|
||||
json={"username": email, "password": password},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
token = resp.json()["id"]
|
||||
return MetabaseClient(base_url, token)
|
||||
@@ -0,0 +1,143 @@
|
||||
"""CRUD de dashboards de Metabase."""
|
||||
|
||||
from .client import MetabaseClient
|
||||
|
||||
|
||||
def metabase_list_dashboards(
|
||||
client: MetabaseClient,
|
||||
filter: str = "",
|
||||
) -> list[dict]:
|
||||
"""Lista dashboards de Metabase con filtro opcional.
|
||||
|
||||
Endpoint: GET /api/dashboard. Retorna dashboards resumidos (sin dashcards).
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
filter: "all", "mine" o "archived". Vacio = todas.
|
||||
|
||||
Returns:
|
||||
Lista de dicts con: id, name, description, collection_id,
|
||||
creator_id, archived, created_at.
|
||||
|
||||
Example:
|
||||
>>> dashboards = metabase_list_dashboards(client, filter="mine")
|
||||
>>> for d in dashboards:
|
||||
... print(d["id"], d["name"])
|
||||
"""
|
||||
params = {}
|
||||
if filter:
|
||||
params["f"] = filter
|
||||
return client.request("GET", "/api/dashboard", params=params)
|
||||
|
||||
|
||||
def metabase_get_dashboard(client: MetabaseClient, dashboard_id: int) -> dict:
|
||||
"""Obtiene un dashboard completo incluyendo sus cards.
|
||||
|
||||
Endpoint: GET /api/dashboard/:id.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
dashboard_id: ID del dashboard.
|
||||
|
||||
Returns:
|
||||
Dict con: id, name, description, dashcards (lista de cards posicionadas),
|
||||
parameters (filtros), tabs, collection_id, archived.
|
||||
|
||||
Cada dashcard tiene: id, card_id, card (objeto completo), size_x, size_y,
|
||||
col, row, dashboard_tab_id, parameter_mappings, visualization_settings.
|
||||
|
||||
Example:
|
||||
>>> dash = metabase_get_dashboard(client, 1)
|
||||
>>> for dc in dash["dashcards"]:
|
||||
... print(f"Card {dc['card_id']} at ({dc['col']}, {dc['row']})")
|
||||
"""
|
||||
return client.request("GET", f"/api/dashboard/{dashboard_id}")
|
||||
|
||||
|
||||
def metabase_create_dashboard(
|
||||
client: MetabaseClient,
|
||||
name: str,
|
||||
description: str = "",
|
||||
collection_id: int = 0,
|
||||
) -> dict:
|
||||
"""Crea un nuevo dashboard vacio en Metabase.
|
||||
|
||||
Endpoint: POST /api/dashboard.
|
||||
Para agregar cards usar metabase_update_dashboard con dashcards.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
name: Nombre del dashboard.
|
||||
description: Descripcion opcional.
|
||||
collection_id: Coleccion destino. 0 = root.
|
||||
|
||||
Returns:
|
||||
Dict con el dashboard creado.
|
||||
|
||||
Example:
|
||||
>>> dash = metabase_create_dashboard(client, "Sales Overview", "KPIs de ventas")
|
||||
>>> # Agregar cards:
|
||||
>>> metabase_update_dashboard(client, dash["id"], dashcards=[
|
||||
... {"id": -1, "card_id": 42, "size_x": 6, "size_y": 4, "col": 0, "row": 0},
|
||||
... ])
|
||||
"""
|
||||
body: dict = {"name": name}
|
||||
if description:
|
||||
body["description"] = description
|
||||
if collection_id > 0:
|
||||
body["collection_id"] = collection_id
|
||||
return client.request("POST", "/api/dashboard", json=body)
|
||||
|
||||
|
||||
def metabase_update_dashboard(client: MetabaseClient, dashboard_id: int, **fields) -> dict:
|
||||
"""Actualiza un dashboard incluyendo metadata, cards y tabs.
|
||||
|
||||
Endpoint: PUT /api/dashboard/:id.
|
||||
|
||||
El campo dashcards representa el ESTADO COMPLETO DESEADO del dashboard:
|
||||
- Agregar card: incluirla con ID negativo (-1, -2, etc.)
|
||||
- Actualizar card existente: incluirla con su ID positivo
|
||||
- Eliminar card: omitirla del array
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
dashboard_id: ID del dashboard.
|
||||
**fields: Campos a actualizar. Validos:
|
||||
name (str), description (str), archived (bool),
|
||||
dashcards (list[dict]), tabs (list[dict]),
|
||||
parameters (list[dict]), collection_id (int).
|
||||
|
||||
Returns:
|
||||
Dict con el dashboard actualizado.
|
||||
|
||||
Example:
|
||||
>>> # Cambiar nombre
|
||||
>>> metabase_update_dashboard(client, 1, name="Updated Name")
|
||||
>>>
|
||||
>>> # Agregar card (primero obtener existentes)
|
||||
>>> dash = metabase_get_dashboard(client, 1)
|
||||
>>> cards = list(dash["dashcards"])
|
||||
>>> cards.append({"id": -1, "card_id": 55, "size_x": 6, "size_y": 4, "col": 0, "row": 0})
|
||||
>>> metabase_update_dashboard(client, 1, dashcards=cards)
|
||||
>>>
|
||||
>>> # Archivar (soft-delete)
|
||||
>>> metabase_update_dashboard(client, 1, archived=True)
|
||||
"""
|
||||
return client.request("PUT", f"/api/dashboard/{dashboard_id}", json=fields)
|
||||
|
||||
|
||||
def metabase_delete_dashboard(client: MetabaseClient, dashboard_id: int) -> None:
|
||||
"""Elimina permanentemente un dashboard.
|
||||
|
||||
Endpoint: DELETE /api/dashboard/:id. IRREVERSIBLE.
|
||||
Para soft-delete preferir: metabase_update_dashboard(client, id, archived=True)
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
dashboard_id: ID del dashboard a eliminar.
|
||||
|
||||
Example:
|
||||
>>> metabase_delete_dashboard(client, 1)
|
||||
>>> # Preferir: metabase_update_dashboard(client, 1, archived=True)
|
||||
"""
|
||||
client.request("DELETE", f"/api/dashboard/{dashboard_id}")
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
name: metabase_auth
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_auth(base_url: str, email: str, password: str) -> MetabaseClient"
|
||||
description: "Autentica contra la API de Metabase con email y password. Retorna un MetabaseClient con session token valido por 14 dias. Endpoint: POST /api/session."
|
||||
tags: [metabase, auth, session, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/client.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
from functions.metabase import metabase_auth
|
||||
|
||||
client = metabase_auth("http://localhost:3000", "admin@example.com", "pass")
|
||||
# client listo para usar con todas las funciones CRUD
|
||||
|
||||
# Alternativa con API key:
|
||||
from functions.metabase import MetabaseClient
|
||||
client = MetabaseClient("http://localhost:3000", "mb_api_key_xxxxx")
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Dos formas de obtener un client:
|
||||
- `metabase_auth()`: login con email/password, obtiene session token via POST /api/session
|
||||
- `MetabaseClient(base_url, api_key)`: constructor directo con API key (recomendado para automatizacion)
|
||||
|
||||
El client es un context manager: `with metabase_auth(...) as client:`
|
||||
|
||||
Errores comunes:
|
||||
- 401: credenciales invalidas
|
||||
- Rate limiting en intentos fallidos de login
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: metabase_create_card
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_create_card(client: MetabaseClient, name: str, dataset_query: dict, display: str = 'table', collection_id: int = 0, description: str = '') -> dict"
|
||||
description: "Crea una card/pregunta en Metabase con query SQL nativa o MBQL. Endpoint: POST /api/card."
|
||||
tags: [metabase, card, question, create, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/cards.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
card = metabase_create_card(client, "Revenue", {
|
||||
"database": 1, "type": "native",
|
||||
"native": {"query": "SELECT SUM(total) FROM orders"},
|
||||
}, display="scalar")
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
dataset_query SQL nativo: `{"database": id, "type": "native", "native": {"query": "..."}}`
|
||||
dataset_query MBQL: `{"database": id, "type": "query", "query": {"source-table": id, ...}}`
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: metabase_create_dashboard
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_create_dashboard(client: MetabaseClient, name: str, description: str = '', collection_id: int = 0) -> dict"
|
||||
description: "Crea dashboard vacio en Metabase. Para agregar cards usar metabase_update_dashboard con dashcards. Endpoint: POST /api/dashboard."
|
||||
tags: [metabase, dashboard, create, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/dashboards.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
dash = metabase_create_dashboard(client, "Sales Overview", "KPIs")
|
||||
# Agregar cards con metabase_update_dashboard
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Se crea vacio. Agregar cards con metabase_update_dashboard(dashcards=[...]).
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: metabase_create_user
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_create_user(client: MetabaseClient, first_name: str, last_name: str, email: str, password: str = '', group_ids: list[int] | None = None) -> dict"
|
||||
description: "Crea un nuevo usuario en Metabase. Sin password envia invitacion por email. Requiere superusuario. Endpoint: POST /api/user."
|
||||
tags: [metabase, user, create, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/users.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
user = metabase_create_user(client, "John", "Doe", "john@example.com", "pass123")
|
||||
user = metabase_create_user(client, "Jane", "Smith", "jane@example.com", group_ids=[1, 3])
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Email debe ser unico. Error 400 si ya existe.
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: metabase_deactivate_user
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_deactivate_user(client: MetabaseClient, user_id: int) -> None"
|
||||
description: "Desactiva (soft-delete) un usuario en Metabase. Reactivar con PUT /api/user/:id/reactivate. Endpoint: DELETE /api/user/:id."
|
||||
tags: [metabase, user, delete, deactivate, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/users.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
metabase_deactivate_user(client, 5)
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Soft-delete. El usuario se puede reactivar.
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: metabase_delete_card
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_delete_card(client: MetabaseClient, card_id: int) -> None"
|
||||
description: "Elimina permanentemente una card/pregunta. IRREVERSIBLE. Preferir archived=True. Endpoint: DELETE /api/card/:id."
|
||||
tags: [metabase, card, question, delete, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/cards.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
metabase_delete_card(client, 42)
|
||||
# Preferir: metabase_update_card(client, 42, archived=True)
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
IRREVERSIBLE. Preferir soft-delete con metabase_update_card(archived=True).
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: metabase_delete_dashboard
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_delete_dashboard(client: MetabaseClient, dashboard_id: int) -> None"
|
||||
description: "Elimina permanentemente un dashboard. IRREVERSIBLE. Preferir archived=True. Endpoint: DELETE /api/dashboard/:id."
|
||||
tags: [metabase, dashboard, delete, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/dashboards.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
metabase_delete_dashboard(client, 1)
|
||||
# Preferir: metabase_update_dashboard(client, 1, archived=True)
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
IRREVERSIBLE. Preferir soft-delete con metabase_update_dashboard(archived=True).
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: metabase_execute_card
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_execute_card(client: MetabaseClient, card_id: int, parameters: list[dict] | None = None) -> dict"
|
||||
description: "Ejecuta la query de una card guardada y retorna resultados con columnas y filas. Soporta parametros. Endpoint: POST /api/card/:id/query."
|
||||
tags: [metabase, card, question, execute, query, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/cards.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
result = metabase_execute_card(client, 42)
|
||||
for row in result["data"]["rows"]:
|
||||
print(row)
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Respuesta: status, row_count, running_time, data.columns, data.rows, data.cols.
|
||||
Limite default: 2000 filas. Para ad-hoc sin card usar metabase_execute_query.
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: metabase_execute_query
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_execute_query(client: MetabaseClient, database_id: int, sql: str, max_results: int = 0) -> dict"
|
||||
description: "Ejecuta query SQL ad-hoc contra Metabase sin guardarla como card. Util para exploracion rapida. Endpoint: POST /api/dataset."
|
||||
tags: [metabase, query, execute, sql, dataset, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/cards.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
result = metabase_execute_query(client, 1, "SELECT * FROM users LIMIT 10")
|
||||
print(f"{result['row_count']} filas en {result['running_time']}ms")
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Misma respuesta que metabase_execute_card. Default 2000 filas, override con max_results.
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: metabase_get_card
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_get_card(client: MetabaseClient, card_id: int) -> dict"
|
||||
description: "Obtiene detalles completos de una card/pregunta de Metabase incluyendo query, visualizacion y metadata. Endpoint: GET /api/card/:id."
|
||||
tags: [metabase, card, question, get, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/cards.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
card = metabase_get_card(client, 42)
|
||||
print(card["name"], card["display"])
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Error 404 si no existe.
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: metabase_get_dashboard
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_get_dashboard(client: MetabaseClient, dashboard_id: int) -> dict"
|
||||
description: "Obtiene dashboard completo con dashcards (cards posicionadas), tabs y parametros. Endpoint: GET /api/dashboard/:id."
|
||||
tags: [metabase, dashboard, get, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/dashboards.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
dash = metabase_get_dashboard(client, 1)
|
||||
for dc in dash["dashcards"]:
|
||||
print(f"Card {dc['card_id']} at ({dc['col']}, {dc['row']})")
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Cada dashcard tiene: id, card_id, card, size_x, size_y, col, row, dashboard_tab_id, parameter_mappings.
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: metabase_get_user
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_get_user(client: MetabaseClient, user_id: int) -> dict"
|
||||
description: "Obtiene los detalles de un usuario de Metabase por su ID. Endpoint: GET /api/user/:id."
|
||||
tags: [metabase, user, get, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/users.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
user = metabase_get_user(client, 1)
|
||||
print(user["email"], user["is_superuser"])
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Error 404 si el usuario no existe.
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: metabase_list_cards
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_list_cards(client: MetabaseClient, filter: str = '', model_id: int = 0) -> list[dict]"
|
||||
description: "Lista preguntas/cards de Metabase. Filtros: all, mine, fav, archived, recent, popular, database, table. Endpoint: GET /api/card."
|
||||
tags: [metabase, card, question, list, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/cards.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
cards = metabase_list_cards(client, filter="mine")
|
||||
cards = metabase_list_cards(client, filter="database", model_id=1)
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
No tiene paginacion offset/limit. Retorna todas las cards que coinciden.
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: metabase_list_dashboards
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_list_dashboards(client: MetabaseClient, filter: str = '') -> list[dict]"
|
||||
description: "Lista dashboards de Metabase. Filtros: all, mine, archived. Retorna resumen sin dashcards. Endpoint: GET /api/dashboard."
|
||||
tags: [metabase, dashboard, list, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/dashboards.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
dashboards = metabase_list_dashboards(client, filter="mine")
|
||||
for d in dashboards:
|
||||
print(d["id"], d["name"])
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Para ver cards de un dashboard usar metabase_get_dashboard.
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: metabase_list_users
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_list_users(client: MetabaseClient, status: str = '', query: str = '', limit: int = 0, offset: int = 0) -> dict"
|
||||
description: "Lista usuarios de Metabase con filtros opcionales por estado, nombre/email y paginacion. Endpoint: GET /api/user."
|
||||
tags: [metabase, user, list, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/users.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
users = metabase_list_users(client, status="active", query="john@")
|
||||
for u in users["data"]:
|
||||
print(u["email"], u["first_name"])
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Retorna dict paginado con data, total, limit, offset.
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: metabase_update_card
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_update_card(client: MetabaseClient, card_id: int, **fields) -> dict"
|
||||
description: "Actualiza campos de una card/pregunta via kwargs. Campos: name, description, display, dataset_query, collection_id, archived. Endpoint: PUT /api/card/:id."
|
||||
tags: [metabase, card, question, update, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/cards.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
metabase_update_card(client, 42, name="New Name", archived=True)
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Soft-delete con `archived=True`. Para delete permanente usar metabase_delete_card.
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
name: metabase_update_dashboard
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_update_dashboard(client: MetabaseClient, dashboard_id: int, **fields) -> dict"
|
||||
description: "Actualiza dashboard incluyendo metadata, cards y tabs via kwargs. dashcards es el estado completo deseado: nuevas con ID negativo, existentes con positivo, omitidas se eliminan. Endpoint: PUT /api/dashboard/:id."
|
||||
tags: [metabase, dashboard, update, cards, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/dashboards.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
# Agregar card
|
||||
dash = metabase_get_dashboard(client, 1)
|
||||
cards = list(dash["dashcards"])
|
||||
cards.append({"id": -1, "card_id": 55, "size_x": 6, "size_y": 4, "col": 0, "row": 0})
|
||||
metabase_update_dashboard(client, 1, dashcards=cards)
|
||||
|
||||
# Archivar
|
||||
metabase_update_dashboard(client, 1, archived=True)
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
dashcards = estado completo. ID negativo = nueva, positivo = existente, omitida = eliminada.
|
||||
Campos: name, description, archived, dashcards, tabs, parameters, collection_id.
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: metabase_update_user
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def metabase_update_user(client: MetabaseClient, user_id: int, **fields) -> dict"
|
||||
description: "Actualiza campos de un usuario en Metabase via keyword arguments. Campos: first_name, last_name, email, is_superuser, group_ids, locale. Endpoint: PUT /api/user/:id."
|
||||
tags: [metabase, user, update, api, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [httpx]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/metabase/users.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
metabase_update_user(client, 5, first_name="Jane", is_superuser=True)
|
||||
metabase_update_user(client, 5, group_ids=[1, 3, 5])
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Solo se modifican los campos pasados como kwargs.
|
||||
@@ -0,0 +1,153 @@
|
||||
"""CRUD de usuarios de Metabase."""
|
||||
|
||||
from .client import MetabaseClient
|
||||
|
||||
|
||||
def metabase_list_users(
|
||||
client: MetabaseClient,
|
||||
status: str = "",
|
||||
query: str = "",
|
||||
limit: int = 0,
|
||||
offset: int = 0,
|
||||
) -> dict:
|
||||
"""Lista usuarios de Metabase con filtros opcionales.
|
||||
|
||||
Endpoint: GET /api/user. Requiere permisos de superusuario.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
status: "active" (default), "deactivated" o "all".
|
||||
query: Filtro por nombre o email.
|
||||
limit: Tamanio de pagina (0 = default Metabase).
|
||||
offset: Offset para paginacion.
|
||||
|
||||
Returns:
|
||||
Dict con estructura paginada:
|
||||
- data: lista de usuarios (id, email, first_name, last_name, is_superuser, etc.)
|
||||
- total: numero total de usuarios que coinciden
|
||||
- limit: tamanio de pagina usado
|
||||
- offset: offset usado
|
||||
|
||||
Example:
|
||||
>>> users = metabase_list_users(client, status="active", query="john@")
|
||||
>>> for u in users["data"]:
|
||||
... print(u["email"], u["first_name"])
|
||||
"""
|
||||
params = {}
|
||||
if status:
|
||||
params["status"] = status
|
||||
if query:
|
||||
params["query"] = query
|
||||
if limit > 0:
|
||||
params["limit"] = limit
|
||||
if offset > 0:
|
||||
params["offset"] = offset
|
||||
return client.request("GET", "/api/user", params=params)
|
||||
|
||||
|
||||
def metabase_get_user(client: MetabaseClient, user_id: int) -> dict:
|
||||
"""Obtiene un usuario de Metabase por su ID.
|
||||
|
||||
Endpoint: GET /api/user/:id.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
user_id: ID numerico del usuario.
|
||||
|
||||
Returns:
|
||||
Dict con datos del usuario: id, email, first_name, last_name,
|
||||
is_superuser, is_active, common_name, date_joined, last_login,
|
||||
group_ids, locale.
|
||||
|
||||
Raises:
|
||||
httpx.HTTPStatusError: 404 si el usuario no existe.
|
||||
|
||||
Example:
|
||||
>>> user = metabase_get_user(client, 1)
|
||||
>>> print(user["email"], user["is_superuser"])
|
||||
"""
|
||||
return client.request("GET", f"/api/user/{user_id}")
|
||||
|
||||
|
||||
def metabase_create_user(
|
||||
client: MetabaseClient,
|
||||
first_name: str,
|
||||
last_name: str,
|
||||
email: str,
|
||||
password: str = "",
|
||||
group_ids: list[int] | None = None,
|
||||
) -> dict:
|
||||
"""Crea un nuevo usuario en Metabase.
|
||||
|
||||
Endpoint: POST /api/user. Requiere permisos de superusuario.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado con permisos admin.
|
||||
first_name: Nombre del usuario.
|
||||
last_name: Apellido del usuario.
|
||||
email: Email unico del usuario.
|
||||
password: Password. Vacio = Metabase envia invitacion por email.
|
||||
group_ids: IDs de grupos a asignar. None = solo grupo default.
|
||||
|
||||
Returns:
|
||||
Dict con el usuario creado (mismos campos que metabase_get_user).
|
||||
|
||||
Raises:
|
||||
httpx.HTTPStatusError: 400 si el email ya existe.
|
||||
|
||||
Example:
|
||||
>>> user = metabase_create_user(client, "John", "Doe", "john@example.com", "pass123")
|
||||
>>> print(user["id"])
|
||||
"""
|
||||
body: dict = {
|
||||
"first_name": first_name,
|
||||
"last_name": last_name,
|
||||
"email": email,
|
||||
}
|
||||
if password:
|
||||
body["password"] = password
|
||||
if group_ids:
|
||||
body["group_ids"] = group_ids
|
||||
return client.request("POST", "/api/user", json=body)
|
||||
|
||||
|
||||
def metabase_update_user(client: MetabaseClient, user_id: int, **fields) -> dict:
|
||||
"""Actualiza campos de un usuario en Metabase.
|
||||
|
||||
Endpoint: PUT /api/user/:id. Requiere permisos de superusuario.
|
||||
Solo se modifican los campos pasados como keyword arguments.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado con permisos admin.
|
||||
user_id: ID del usuario a actualizar.
|
||||
**fields: Campos a actualizar. Validos:
|
||||
first_name (str), last_name (str), email (str),
|
||||
is_superuser (bool), group_ids (list[int]),
|
||||
locale (str), login_attributes (dict).
|
||||
|
||||
Returns:
|
||||
Dict con el usuario actualizado.
|
||||
|
||||
Example:
|
||||
>>> user = metabase_update_user(client, 5, first_name="Jane", is_superuser=True)
|
||||
>>> user = metabase_update_user(client, 5, group_ids=[1, 3, 5])
|
||||
"""
|
||||
return client.request("PUT", f"/api/user/{user_id}", json=fields)
|
||||
|
||||
|
||||
def metabase_deactivate_user(client: MetabaseClient, user_id: int) -> None:
|
||||
"""Desactiva (soft-delete) un usuario en Metabase.
|
||||
|
||||
Endpoint: DELETE /api/user/:id. Requiere permisos de superusuario.
|
||||
El usuario no se elimina permanentemente, solo se marca como inactivo.
|
||||
Para reactivar: PUT /api/user/:id/reactivate.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado con permisos admin.
|
||||
user_id: ID del usuario a desactivar.
|
||||
|
||||
Example:
|
||||
>>> metabase_deactivate_user(client, 5)
|
||||
>>> # Para ver desactivados: metabase_list_users(client, status="deactivated")
|
||||
"""
|
||||
client.request("DELETE", f"/api/user/{user_id}")
|
||||
Reference in New Issue
Block a user