feat(metabase): nuevos modulos — snippets, notifications, dashboard_filters
Tres modulos nuevos con funciones CRUD completas: - snippets: list, get, create, update, archive (SQL reutilizable) - notifications: list, create_card_alert, create_dashboard_subscription, update, delete - dashboard_filters: add_dashboard_filter (parameter_mappings sobre cards existentes) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
"""CRUD de SQL snippets de Metabase.
|
||||
|
||||
Un snippet es un fragmento SQL reutilizable que se referencia en queries
|
||||
nativas con la sintaxis {{snippet: nombre}}. Ideal para CTEs comunes
|
||||
que se repiten en multiples cards.
|
||||
"""
|
||||
|
||||
from .client import MetabaseClient
|
||||
|
||||
|
||||
def metabase_list_snippets(client: MetabaseClient, archived: bool = False) -> list[dict]:
|
||||
"""Lista SQL snippets de Metabase.
|
||||
|
||||
Endpoint: GET /api/native-query-snippet.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
archived: Si True, incluye snippets archivados.
|
||||
|
||||
Returns:
|
||||
Lista de dicts con: id, name, content, description, collection_id,
|
||||
creator_id, archived, created_at, updated_at.
|
||||
|
||||
Example:
|
||||
>>> snippets = metabase_list_snippets(client)
|
||||
>>> for s in snippets:
|
||||
... print(s["name"], len(s["content"]))
|
||||
"""
|
||||
params = {}
|
||||
if archived:
|
||||
params["archived"] = "true"
|
||||
return client.request("GET", "/api/native-query-snippet", params=params)
|
||||
|
||||
|
||||
def metabase_get_snippet(client: MetabaseClient, snippet_id: int) -> dict:
|
||||
"""Obtiene un SQL snippet de Metabase por su ID.
|
||||
|
||||
Endpoint: GET /api/native-query-snippet/:id.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
snippet_id: ID numerico del snippet.
|
||||
|
||||
Returns:
|
||||
Dict con campos completos del snippet: id, name, content, description,
|
||||
collection_id, creator_id, archived, created_at, updated_at.
|
||||
|
||||
Raises:
|
||||
httpx.HTTPStatusError: 404 si el snippet no existe.
|
||||
|
||||
Example:
|
||||
>>> snippet = metabase_get_snippet(client, 42)
|
||||
>>> print(snippet["name"], snippet["content"])
|
||||
"""
|
||||
return client.request("GET", f"/api/native-query-snippet/{snippet_id}")
|
||||
|
||||
|
||||
def metabase_create_snippet(
|
||||
client: MetabaseClient,
|
||||
name: str,
|
||||
content: str,
|
||||
description: str = "",
|
||||
collection_id: int = 0,
|
||||
) -> dict:
|
||||
"""Crea un nuevo SQL snippet en Metabase.
|
||||
|
||||
Endpoint: POST /api/native-query-snippet.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
name: Nombre del snippet. Se usa en queries con {{snippet: nombre}}.
|
||||
content: SQL del snippet. Puede ser una CTE completa, una subquery,
|
||||
o cualquier fragmento SQL reutilizable.
|
||||
description: Descripcion opcional del snippet.
|
||||
collection_id: ID de la coleccion donde guardar el snippet.
|
||||
Si es 0, se guarda en la raiz (Our analytics).
|
||||
|
||||
Returns:
|
||||
Dict con el snippet creado, incluyendo el campo "id" asignado
|
||||
por Metabase.
|
||||
|
||||
Raises:
|
||||
httpx.HTTPStatusError: 400 si el nombre ya existe o el SQL es invalido.
|
||||
|
||||
Example:
|
||||
>>> snippet = metabase_create_snippet(
|
||||
... client,
|
||||
... "supply_orders_cte",
|
||||
... '''WITH supply_full AS (
|
||||
... SELECT so.id, so.service_request_id
|
||||
... FROM supply_orders so
|
||||
... LEFT JOIN service_requests sr ON so.service_request_id = sr.id
|
||||
... )''',
|
||||
... description="CTE base de supply_orders con JOINs para cruce con NAV",
|
||||
... )
|
||||
>>> print(snippet["id"], snippet["name"])
|
||||
>>> # Usar en una card:
|
||||
>>> # "{{snippet: supply_orders_cte}} SELECT ... FROM supply_full"
|
||||
"""
|
||||
body: dict = {"name": name, "content": content}
|
||||
if description:
|
||||
body["description"] = description
|
||||
if collection_id:
|
||||
body["collection_id"] = collection_id
|
||||
return client.request("POST", "/api/native-query-snippet", json=body)
|
||||
|
||||
|
||||
def metabase_update_snippet(
|
||||
client: MetabaseClient,
|
||||
snippet_id: int,
|
||||
**fields,
|
||||
) -> dict:
|
||||
"""Actualiza campos de un SQL snippet en Metabase.
|
||||
|
||||
Endpoint: PUT /api/native-query-snippet/:id.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
snippet_id: ID numerico del snippet a actualizar.
|
||||
**fields: Campos a modificar. Campos validos:
|
||||
- name (str): Nuevo nombre del snippet.
|
||||
- content (str): Nuevo SQL del snippet.
|
||||
- description (str): Nueva descripcion.
|
||||
- collection_id (int): Nueva coleccion.
|
||||
- archived (bool): True para archivar, False para desarchivar.
|
||||
|
||||
Returns:
|
||||
Dict con el snippet actualizado.
|
||||
|
||||
Raises:
|
||||
httpx.HTTPStatusError: 404 si el snippet no existe.
|
||||
httpx.HTTPStatusError: 400 si los campos son invalidos.
|
||||
|
||||
Example:
|
||||
>>> updated = metabase_update_snippet(
|
||||
... client, 42,
|
||||
... content="WITH supply_full AS (SELECT * FROM supply_orders)",
|
||||
... description="Version simplificada",
|
||||
... )
|
||||
>>> print(updated["updated_at"])
|
||||
"""
|
||||
valid_fields = {"name", "content", "description", "collection_id", "archived"}
|
||||
body = {k: v for k, v in fields.items() if k in valid_fields}
|
||||
return client.request("PUT", f"/api/native-query-snippet/{snippet_id}", json=body)
|
||||
|
||||
|
||||
def metabase_archive_snippet(client: MetabaseClient, snippet_id: int) -> dict:
|
||||
"""Archiva un SQL snippet en Metabase.
|
||||
|
||||
Wrapper sobre metabase_update_snippet con archived=True.
|
||||
Los snippets archivados no aparecen en el autocomplete de queries
|
||||
pero sus referencias existentes siguen funcionando.
|
||||
|
||||
Endpoint: PUT /api/native-query-snippet/:id.
|
||||
|
||||
Args:
|
||||
client: Cliente autenticado.
|
||||
snippet_id: ID numerico del snippet a archivar.
|
||||
|
||||
Returns:
|
||||
Dict con el snippet archivado.
|
||||
|
||||
Raises:
|
||||
httpx.HTTPStatusError: 404 si el snippet no existe.
|
||||
|
||||
Example:
|
||||
>>> result = metabase_archive_snippet(client, 42)
|
||||
>>> print(result["archived"]) # True
|
||||
"""
|
||||
return metabase_update_snippet(client, snippet_id, archived=True)
|
||||
Reference in New Issue
Block a user