0014 — Analytics BI (Metabase): auto-provisão via API REST¶
Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-07-03 · Decidido em: 2026-07-03
Contexto¶
A feature analytics da Central de Apps provisiona o Aptabase (eventos —
decisão 0013). A 0013 descopou o Metabase ("BI independente; o app
não o consome"), então a estrutura de BI (collection do cliente, dashboard do app, cards com queries
ClickHouse sobre a tabela events) era criada na mão — repetitivo, propenso a erro e a
divergência entre apps.
Diferente do Aptabase (sem API de provisão), o Metabase tem API REST real — dá pra provisionar de forma robusta e idempotente. Esta decisão reverte o descopo e implementa o passo Metabase.
Decisão¶
- Metabase re-escopado para dentro de
analytics.FEATURE_PROVIDERS["analytics"] = ["aptabase", "metabase"]; a resposta honesta (0.17.6) reporta os dois sub-estados (state/last_errorpor provider); a falha de um não impede o outro. MetabaseProvidergarante (procura-antes-de-cria §7, organização em 2 níveis): Collection do grupo/cliente (nome =cliente_id; sem cliente →"X-Adm"; match por nome na raiz + não-archived, pois nome de collection não é único) → sub-pasta do app (nome =app_id,parent_id= a do grupo/cliente) → Dashboard do app (porapp_id) → cards baseline (query nativa ClickHouse, dbMETABASE_CLICKHOUSE_DB_IDdefault2,<APP_ID>substituído), tudo dentro da sub-pasta do app (X-Adm|cliente → app → dashboard + cards).- Cards com nome canônico, sem sufixo — já isolados na sub-pasta do app, então não colidem entre
apps (a sub-pasta substitui o antigo sufixo
· app_id). Idempotência pelosdashcardsdo dashboard + reuso por nome na collection (adota cards órfãos de uma falha parcial, sem duplicar); ligados por read-modify-write (GET /api/dashboard/:id→ mescla →PUT <!-- checa-rotas: ignorar --> /api/dashboard/:idcom dashcards de id negativo único — a versão do Metabase rejeitaid:-1repetido; oPOST /:id/cardsé deprecado ≥ v0.47). - Baseline 5 sempre (Usuários Ativos, Sessões Ativas, Usuários Ativos por Dia, Versão do
App, Ações Mais Usadas — que já agrega todo evento custom via
NOT IN). + Telas Mais Usadas só sescreen_view ∈ eventos(o app manda oseventosna provisão; ausente/vazio → inclui). OAppcarregaeventoscomo campo transiente (não persiste). - Período = duas variáveis
data_inicio/data_fim(type:date) — não field filter (evita lookup defield_ide dependência do sync do ClickHouse no Metabase). - BI = só link: devolve
metabase_dashboard_id(client-grade →app.json); o app não embeda (sem signed embedding). Idempotência entre runs = reuso-persistido (allowlist{aptabase, metabase}noProvisioningService).Superado em parte pela decisão 0017: o "não embeda / sem signed embedding" foi revertido — agora o auth habilita
enable_embeddinge minta a URL de embed assinada (AppLinks.metabase_embed), mantendo o link como fallback. O resto da 0014 segue.
Alternativas consideradas¶
- Manter o Metabase fora (0013): provisão manual não escala; a API existe → automatizável.
- Field filter de range (
{{periodo}}→ um widget): exigefield_iddeevents.timestamp+ ClickHouse sincronizado — mais frágil. Preterido pelas duas variáveis. - Criar os 6 cards incondicionalmente: simples, mas Telas viria vazia sem
screen_view. Preterido — o app já informa oseventos. - Cards bespoke por evento custom: deferido (editorial, não automatizável; o Ações Mais Usadas já cobre os custom).
- Signed embedding no app: fora de escopo (mais superfície; o link basta).
Consequências / riscos¶
- Versão mínima do Metabase (guarda): API keys (
x-api-key) exigem ≥ 0.49;PUTcomdashcardsexige ≥ 0.47/0.48. Versão fixada (reconferir em upgrade); instância mais velha → 401/404 → falha honesta (fail-fast, nunca id falso). - Match de collection por nome (não-único): mitigado por raiz + não-archived +
collection_idpersistido (reuso); risco residual aceito. visualization_settingsmínimo por tipo — card pode renderizar pobre (não quebra); ajuste fino é operação.
Operar (config no recurso auth)¶
Provisão automática via /xadm-setup (feature analytics). Sem runbook separado (provider
automagico não tem procedimento manual). Envs a setar uma vez no recurso auth (Coolify):
| Variável | Valor | Secret? |
|---|---|---|
METABASE_URL |
https://metabase.xadm.biz |
não |
METABASE_API_KEY |
API key de escrita (Admin → API Keys; grupo com escrita) | sim ✅ |
METABASE_CLICKHOUSE_DB_ID |
id do DB ClickHouse "Aptabase Clickhouse" (default 2) |
não |
Ausência de METABASE_URL/METABASE_API_KEY → o provider recusa (boot ok). Diagnóstico: o
/provision responde providers[].last_error (ex.: Metabase POST /api/card HTTP 401 = API key
inválida/sem escrita; HTTP 404 = versão do Metabase antiga ou db id errado).
Lado do app (fora do auth)¶
O /xadm-setup manda os eventos do app no request de provisão e grava
features.analytics.metabase_dashboard_id no app.json (link pro dashboard). Ver
decisão 0013 para o lado Aptabase da mesma feature.