Files
fn_registry/python/functions/metabase/snippets.py
T
egutierrez cb392a48ee 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>
2026-04-14 19:03:08 +02:00

171 lines
5.5 KiB
Python

"""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)