Integração com orquestradores e integradores (dbt Cloud, dbt Core, Airflow, Fivetran, Airbyte)
O BeryX liga-se aos orquestradores de duas formas complementares (e lê a saúde dos pipelines dos integradores Fivetran e Airbyte — §3):
| dbt Cloud | Airflow | |
|---|---|---|
| Como | Webhook do dbt Cloud → POST /api/v1/webhooks/dbt-cloud/{id} |
Pacote beryx-airflow (operator, sensor, callbacks) |
| Dispara scan | Automaticamente quando um job run conclui, nos datasets do projeto mapeado | BeryXScanOperator numa DAG, espera pelo veredito |
| Contexto no RCA | Cada run (sucesso/erro/cancelado) fica guardado | Os callbacks reportam o estado das tasks |
| Autenticação | Assinatura HMAC-SHA256 do dbt Cloud | Token de API dvk_* |
Com isto, o diagnóstico de causa-raiz (RCA) de uma issue passa a dizer, por exemplo: «o job dbt «Daily Job (dbt build)» falhou 20 min antes desta issue» — tanto no relatório IA como no template offline.
Com o dbt Core (§4), o caminho é o inverso: o dbt test corre no teu pipeline,
no teu warehouse, e devolve os resultados ao BeryX — as regras exportadas como
testes dbt voltam como resultados dessas regras.
1. dbt Cloud — webhook «job run concluído»
1.1 Criar o webhook no dbt Cloud
- No dbt Cloud: Account settings → Webhooks → Create webhook.
- Eventos:
Run completed(obrigatório — é o que dispara o scan). OpcionalmenteRun startedeRun errored, para o contexto do RCA ficar mais rico. - Jobs: os que carregam as tabelas monitorizadas pelo BeryX.
- Endpoint: ainda não o tens — primeiro configura o BeryX (1.2) e depois volta aqui com o URL. O dbt Cloud mostra o Secret do webhook ao guardar: copia-o.
1.2 Configurar o BeryX (admin)
curl -X PUT https://api.praxis.lbostuff.tech/api/v1/integrations/dbt-cloud \
-H "Authorization: Bearer $JWT_ADMIN" -H "Content-Type: application/json" \
-d '{
"secret": "<Secret do webhook dbt Cloud>",
"account_id": "<id da conta dbt Cloud (opcional, recusa eventos de outras contas)>",
"project_mappings": [
{"project_id": "167194", "connection_id": "<id da conexão BeryX>"},
{"project_id": "170001", "connection_id": "<id>", "dataset_ids": ["<id>", "<id>"]}
]
}'
A resposta traz o webhook_path (/api/v1/webhooks/dbt-cloud/<id>) — o URL
completo a colar no dbt Cloud é https://api.praxis.lbostuff.tech + esse path.
- Mapeamento projeto dbt → conexão: cada
project_iddo dbt Cloud aponta para uma conexão do workspace;dataset_ids(opcional) limita o scan a esses datasets — sem ele, o scan cobre a conexão inteira. - Segredo: cifrado com Fernet antes de persistir; nunca é devolvido pela API
(
has_secret: true) nem escrito nos logs.PUTsemsecretmantém o atual; para rodar, envia o novo segredo. GET /integrations/dbt-cloudmostra a configuração elast_delivery_at;DELETEremove-a. Alterações ficam na auditoria (dbt_cloud_configured).
1.3 O que acontece em cada evento
| Evento dbt Cloud | Efeito |
|---|---|
job.run.started |
Run guardado como running |
job.run.errored |
Run guardado como error (hora do erro em runErroredAt) |
job.run.completed |
Run guardado com o estado final (success/error/cancelled) e pedido de scan (trigger = dbt_cloud) dos datasets do projeto mapeado |
| outros | Aceites e ignorados (status: ignored) |
- Verificação da assinatura: o header
Authorizationtem de ser o HMAC-SHA256 (hex) do corpo cru com o segredo. Assinatura errada →401; integração inexistente ou desligada →404. - Idempotência: re-entregas atualizam o mesmo run (
workspace + run_id) e só o primeirocompleteddispara scan (scan: already_requested). Umstartedque chegue atrasado não faz um run terminado voltar arunning. - O scan corre mesmo quando o job dbt falhou: tabelas por atualizar aparecem como issues de frescura/volume, com o run falhado no RCA.
- Nunca falha a entrega por causa do scan: projeto não mapeado
(
unmapped_project), quota do plano esgotada (plan_limit), scans pedidos em fila no máximo do plano (backpressure, CAPACIDADE.md §3) ou mensageria em baixo (messaging_unavailable) →200com o motivo emscan; o run fica guardado como contexto na mesma. Com200o dbt Cloud não reenvia, por isso o scan desse run não é repetido: pede-o o próximo run concluído do job, o scan agendado da conexão ou um pedido manual. - Dados guardados: ids e nomes de job/projeto/ambiente, estado, mensagem de
estado (≤ 500 caracteres) e horas. O
runReason(que traz o email de quem lançou o run) é descartado.
1.4 Testar localmente
BODY='{"eventType":"job.run.completed","accountId":1,"data":{"runId":"1","projectId":"167194","jobName":"teste","runStatus":"Success","runStatusCode":10}}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SEGREDO" -hex | sed 's/^.* //')
curl -X POST http://localhost:8000/api/v1/webhooks/dbt-cloud/<id> \
-H "Authorization: $SIG" -H "Content-Type: application/json" -d "$BODY"
1.5 Runs e freshness declarada vs observada (Discovery API)
O webhook diz quando um job acaba. A Discovery API do dbt Cloud diz o
que o dbt acha de cada tabela: o último run de cada modelo e o resultado da
dbt source freshness (critérios warn_after/error_after declarados). O
BeryX cruza isso com a frescura que observa na fonte (última
modificação da tabela, recolhida em cada scan pelos monitores automáticos) e
abre um issue quando os dois discordam — «o dbt diz fresh, os dados dizem 26h».
- No dbt Cloud: Account settings → Service tokens → New token, permissão Metadata Only (só leitura de metadados).
- Descobre o
environment_iddo ambiente de produção (Deploy → Environments, o número no URL). - Acrescenta à configuração:
curl -X PUT https://api.praxis.lbostuff.tech/api/v1/integrations/dbt-cloud \
-H "Authorization: Bearer $JWT_ADMIN" -H "Content-Type: application/json" \
-d '{
"api_token": "<service token Metadata Only>",
"metadata_api_url": "https://metadata.cloud.getdbt.com/graphql",
"project_mappings": [
{"project_id": "167194", "connection_id": "<id>", "environment_id": "218762"}
]
}'
metadata_api_urlé opcional (por omissão o multi-tenant US). Contas EMEA/AU ou cell-based usam o URL que o dbt Cloud mostra em Account settings → Access URLs (ex.:https://ab123.metadata.us1.dbt.com/graphql). Só hosts*.getdbt.com/*.dbt.com, sempre https.- O token é cifrado (Fernet) e nunca devolvido (
has_api_token: true).PUTsemapi_tokenmantém-no;"api_token": ""remove-o. PUTsemmetadata_api_urlmantém o URL guardado;"metadata_api_url": ""volta ao multi-tenant US. O token guardado só segue para o host com que foi guardado: mudar o URL exige oapi_tokenno mesmo pedido (ou""para o remover), senão a resposta é400.- Quando corre: de hora a hora no worker, ou já com
POST /integrations/dbt-cloud/metadata-sync(admin). Erros do dbt Cloud (token recusado, ambiente inexistente) ficam emmetadata_sync_error.
O que é comparado (sempre no instante do check/run do dbt, nunca extrapolado):
| Divergência | Quando | Severidade |
|---|---|---|
fresh_but_stale — «dbt diz fresh, os dados dizem 26h» |
Source freshness pass, mas a tabela não é modificada há mais do que o critério declarado (o loaded_at_field não reflete as cargas, ou a carga não escreveu nada) |
aviso |
stale_but_fresh |
Source freshness warn/error, mas a tabela foi modificada dentro do critério |
info |
run_not_reflected |
Último run de um modelo table com sucesso, mas a tabela não é modificada desde antes do run |
aviso |
- Os nós do dbt casam com os datasets da conexão mapeada por
schema+alias/identifier. - Sem critério declarado, sem observação posterior ao check/run, modelos
view/incrementalou runs falhados →not_comparable(com o motivo); os runs falhados já entram no RCA pelo webhook. - Um issue
dbt_divergencepor dataset e tipo: é atualizado a cada sync, resolve-se sozinho quando os dois lados voltam a concordar e fica calado se for marcado «ignorado». GET /integrations/dbt-cloud/node-states?verdict=divergentlista o estado por dataset: último run, freshness declarada, frescura observada e veredito.- Só metadados: nunca SQL compilado, logs de runs nem linhas.
2. Airflow — pacote beryx-airflow
Código em integrations/beryx_airflow/ (publica-se no PyPI pelo sdk-publish.yml
com package=airflow — sdk/PUBLISHING.md; ver o README do pacote). Airflow 2.6+ e 3.x; o cliente HTTP usa só a stdlib.
2.1 Ligação
Cria um token de API na página Equipa e uma Connection no Airflow:
airflow connections add beryx_default \
--conn-type beryx \
--conn-host https://api.praxis.lbostuff.tech \
--conn-password "$DATAVAULT_TOKEN" # dvk_… — guardado no secrets backend
Os tokens dvk_* são de leitura, com três exceções explícitas: POST /rag-gate,
POST /scans (sujeito à quota) e POST /orchestrator-runs.
2.2 Gate de qualidade numa DAG
from beryx_airflow import BeryXScanOperator, report_to_beryx
report = report_to_beryx(connection_id="<id da conexão BeryX>")
with DAG(
"orders_daily",
default_args={"on_failure_callback": report, "on_success_callback": report},
...
):
quality_gate = BeryXScanOperator(
task_id="beryx_quality_gate",
connection_id="<id da conexão BeryX>",
dataset_ids=["<id fct_orders>"], # omitido => todos os datasets da conexão
fail_on="fail", # "warn" chumba também em warnings
)
load >> transform >> quality_gate >> publish
- O operator dispara o scan, espera que termine (
poll_interval,scan_timeout) e lêGET /datasets/{id}/verdictde cada dataset. O pior veredito ganha; a task falha emfail(ouwarn, comfail_on="warn") e quando o scan falha. - XCom
return_value:{scan_id, scan_status, verdict, datasets[]}. - Para não ocupar um worker durante o scan:
wait_for_completion=False+BeryXScanSensor(scan_id=..., mode="reschedule"). report_to_beryxé best-effort (nunca falha a DAG) e envia só metadados: dag/task/run, estado, horas e a classe da exceção — nunca a mensagem.
3. Integradores — Fivetran e Airbyte (saúde de pipelines)
O dbt e o Airflow transformam; o Fivetran e o Airbyte trazem os dados da origem para o destino. O BeryX lê — só leitura, pelas APIs oficiais — o que o integrador sabe de cada pipeline (último sync, estado, falhas, cadência, tabelas que escreve) e junta-lhe o que observa nas duas pontas:
- estado por dataset de destino:
ok,failed(o último sync falhou),delayed(sem sucesso há mais do que o tolerado),pausedouunknown; - latência origem→destino: «origem atualizada há 20 min, destino há 5h» — e
lag_s, quanto o destino vai atrás da origem; - issues
pipeline_sync(failed/delayed), um por dataset e subtipo; - contexto no RCA: cada sync fica em
orchestrator_runs(sourcefivetranouairbyte), ligado à conexão de destino — o diagnóstico passa a dizer «o sync Fivetran «salesforce» falhou 40 min antes desta issue»; - reconciliação automática origem↔destino (RECON v1) quando a conexão de origem também está ligada ao BeryX e as duas pontas têm perfil.
3.1 Credenciais (só leitura)
| Fivetran | Airbyte | |
|---|---|---|
| Credencial | Chave + segredo da API (Account settings → API key) de um utilizador ou papel com leitura das connections | Client id + client secret de uma application (Settings → Applications) |
| Host | https://api.fivetran.com (fixo) |
Airbyte Cloud https://api.airbyte.com (omissão) ou o URL da instância self-managed, ex. https://airbyte.empresa.pt/api/public |
Âmbito opcional (scope_id) |
group_id (destino Fivetran) |
workspace_id do Airbyte |
| Chamadas | GET /v1/connections (paginado por cursor), GET /v1/connections/{id}/schemas |
POST /v1/applications/token (troca de credenciais por token — o único POST, numa allowlist fechada), GET /v1/connections, GET /v1/jobs?connectionId=…&jobType=sync |
- Nenhum pedido altera o integrador: não lançamos syncs, não mudamos schemas nem agendas. Todos os GET passam pelo cliente HTTP read-only com o guard anti-SSRF; um POST fora da allowlist é recusado antes de tocar na rede.
- As credenciais são cifradas (Fernet) e nunca devolvidas nem escritas nos logs; as mensagens de erro do integrador são cortadas a 500 caracteres.
- O URL do Airbyte self-managed tem de ser https; metadata da cloud é sempre
bloqueada e redes privadas só onde o ambiente as permite (e com o host em
DATAVAULT_CONNECTOR_ALLOWED_HOSTS). - Mudar o URL (incluindo Cloud ↔ self-managed) exige reenviar
client_ideclient_secretno mesmoPATCH(senão 400): as credenciais guardadas nunca seguem para um host novo. A auditoria regista o URL antigo e o novo. - Cada sync tem um orçamento de 300 pedidos, retries limitados em 429/5xx
(
Retry-Afteraté 10 s) e no máximo 500 pipelines; o que ficar por ler vem no próximo (status: partial).
3.2 Configurar (admin)
curl -X POST https://api.praxis.lbostuff.tech/api/v1/integrations/pipelines/fivetran \
-H "Authorization: Bearer $JWT_ADMIN" -H "Content-Type: application/json" \
-d '{
"name": "Fivetran produção",
"credentials": {"api_key": "<chave>", "api_secret": "<segredo>"},
"scope_id": "<group_id (opcional)>",
"destination_connection_id": "<id da conexão do warehouse no BeryX>",
"pipeline_mappings": [
{"pipeline_id": "<id da connection Fivetran>",
"source_connection_id": "<id da conexão de origem, se o BeryX a monitoriza>",
"max_delay_minutes": 180}
]
}'
Para o Airbyte, POST /integrations/pipelines/airbyte com
"credentials": {"client_id": "…", "client_secret": "…"} e, se for self-managed,
"base_url".
- Destino:
destination_connection_idé a conexão para onde escrevem os pipelines por omissão; um mapeamento pode indicar outra (destination_connection_id) e o schema de destino (destination_schema) quando o integrador não o diz (Airbyte com namespace do destino). - Origem (opcional): com
source_connection_id, a tabela de origem casa com o dataset da conexão de origem — latência e reconciliação. - Atraso tolerado: por omissão 2 × a cadência declarada + 15 min (Fivetran
sync_frequency; Airbyte agendabasic). Pipelines com agenda cron/manual só ficam «atrasados» commax_delay_minutes(5 min–7 dias) ou quando o próprio Fivetran os marca atrasados. GET /integrations/pipelineslista as integrações com o retrato do último sync por pipeline;PATCH/DELETE …/{provider}/{id}alteram/removem (remover resolve os issues que a integração abriu);POST …/testvalida as credenciais;POST …/syncsincroniza já (o worker fá-lo de hora a hora, e uma falha inesperada numa integração não trava as seguintes). Tudo na auditoria (pipeline_integration_*), sem credenciais.
3.3 O que é comparado
- Casamento: tabela do integrador (schema + nome no destino) ↔ dataset da conexão de destino, sem distinguir maiúsculas (o Snowflake põe tudo em maiúsculas); sem schema conhecido, só pelo nome e só se for único.
- Falhado: a última falha é mais recente do que o último sucesso — a mensagem do integrador (task do Fivetran) vai para o issue e para o RCA. A API pública do Airbyte não expõe a mensagem de erro de um job.
- Atrasado: sem sucesso há mais do que o tolerado.
- Latência:
source.updated_at= última modificação observada na origem (monitores automáticos);destination.updated_at= último sync bem-sucedido. - Reconciliação:
reconcile_countsentre os perfis mais recentes das duas pontas (tolerância 1 %).comparable: truesó quando a origem foi contada antes do último sync e o destino depois; senão a diferença pode ser só dados novos. Não abre issue. As linhas que o integrador diz ter sincronizado são incrementais e nunca entram na conta. - Mudanças de schema: tabelas que entraram ou saíram do pipeline entre dois
syncs (
schema_changeno retrato do pipeline e na saúde do dataset). - Um issue
pipeline_syncpor dataset e subtipo: atualizado a cada sync, resolve-se sozinho quando o sync volta ao normal e fica calado se for marcado «ignorado». Severidade aviso; dimensão atualidade (delayed) ou disponibilidade (failed). - Volume: o Fivetran e o Airbyte não dão linhas por tabela nas APIs REST
públicas; o Airbyte dá linhas por sync (
pipeline_rows_synced).
GET /datasets/{id}/pipeline-health devolve tudo isto por dataset, com os syncs
recentes. ADF e Talend são as fatias seguintes (mesmo contrato
PipelineMetadataConnector).
4. dbt Core — resultados do dbt test (run_results.json)
O BeryX exporta as regras como testes dbt (GET /datasets/{id}/rules.dbt.yml e,
para as regras SQL, rules.dbt-tests.zip). Cada teste exportado leva a regra de
origem em config.meta.beryx.rule_id. Os testes exportados antes da API 1.77.0 têm
config.meta.datavault.rule_id, que continua a casar até 31 de maio de 2027.
Quando o teu pipeline corre dbt test ou
dbt build, envia o target/run_results.json e o target/manifest.json a
POST /api/v1/integrations/dbt/run-results. Os resultados aparecem onde geres a
qualidade: no veredito do gate de CI, nos issues e nas atividades. O round-trip
deixa de ser só de definições.
4.1 Enviar os resultados
Com o SDK (pip install beryx-sdk) e um token de API dvk_*:
dbt build # ou dbt test
DATAVAULT_TOKEN=dvk_... DATAVAULT_API_URL=https://api.exemplo.pt \
beryx dbt push-results --run-results target/run_results.json
Sem --manifest, o comando usa o manifest.json da mesma pasta. Com
--connection <id>, só casa com os datasets dessa conexão. Sai com 0 quando envia
(ou quando aquela invocação já tinha sido enviada) e com 2 em erro.
Passo de CI (GitHub Actions; o mesmo em GitLab/Jenkins):
- name: dbt build
run: dbt build --target prod
- name: Resultados para o BeryX
if: always() # envia também quando há testes a falhar
env:
DATAVAULT_TOKEN: ${{ secrets.DATAVAULT_TOKEN }}
DATAVAULT_API_URL: ${{ vars.DATAVAULT_API_URL }}
run: beryx dbt push-results --run-results target/run_results.json
Hook on-run-end (dbt_project.yml). O dbt só escreve o run_results.json
depois dos hooks, por isso o hook não o pode enviar. Usa o passo a seguir ao
comando, como acima, ou um wrapper:
dbt test; status=$?
beryx dbt push-results --run-results target/run_results.json || true
exit $status
Sem o SDK, o corpo é um objeto JSON com os dois ficheiros:
jq -n --slurpfile r target/run_results.json --slurpfile m target/manifest.json \
'{run_results: $r[0], manifest: $m[0]}' |
curl -sf -X POST "$DATAVAULT_API_URL/api/v1/integrations/dbt/run-results" \
-H "Authorization: Bearer $DATAVAULT_TOKEN" -H "Content-Type: application/json" -d @-
4.2 Como cada teste é lido
- Dataset: o nó testado casa por schema + alias/identifier, sem distinguir
maiúsculas, como no §1.5. Um schema diferente do catálogo não casa pelo nome.
Só casa com os datasets em que o utilizador do token (ou da sessão) pode
escrever: com domínios restritos, um dataset fora do teu acesso, ou só com
leitura, fica como
no_dataset, igual a um que não existe. Nada é gravado nele e a resposta não traz odataset_idnem orule_id. - Regra: primeiro pelo
rule_idemconfig.meta.beryxou, até 31 de maio de 2027, emconfig.meta.datavault. Com as duas, vale aberyx. Esse id só conta se a regra for do teu workspace, estiver ativa e for do dataset casado. Depois, pela assinatura: coluna + tipo de teste mapeado para o tipo de regra (not_null,unique,accepted_values, regex, intervalo, validade), a mesma do GAP do import do manifest. - Com regra: um resultado dessa regra (
engine: dbt). Conta paraGET /datasets/{id}/verdict: uma regra crítica a falhar no dbt dáfail, e o envio seguinte compasslimpa-o. O veredito é o do último scan do BeryX, com o resultado dbt mais recente de cada regra por cima. Umdbt testcom uma só regra não esconde as falhas do scan. Uma falha abre ou atualiza o mesmo issuerule_violationque o scan abriria. Umpassresolve-o se a última evidência veio do dbt. - Sem regra equivalente (testes singulares,
relationships, testes de pacotes sem mapeamento): issuerule_violationcomsubtypedbt_teste o nome do teste.fail/error→ crítico,warn→ aviso,passresolve. Um issue ignorado silencia esse teste.
Testes skipped, nós que não são testes (models de um dbt build) e testes que
não estão no manifest enviado são contados no relatório e não mexem em nada.
4.3 Garantias
- Idempotente: o
invocation_idfica como um rundbt_coreemGET /orchestrator-runs(contexto do RCA). Reenviar a mesma invocação devolvestatus: duplicatee não grava nada. - Só metadados de execução: o schema é fechado (as chaves do
run_results.jsonv4 a v6, dbt 1.3 a 1.10). Uma chave desconhecida, um campo com linhas (failed_rows,rows, …) ou umfailuresque não seja uma contagem dão422. O SQL compilado, as mensagens, oadapter_responsee osargsservem para validar e não são gravados nem logados. Comstore_failures, as linhas ficam no teu warehouse. - Nada é lido da fonte: os resultados entram num scan
dbtsintético por conexão, que não conta para a quota mensal de scans do plano. - Teto: 60 MB por pedido (o manifest vai junto). Nesta versão, o manifest é obrigatório.
5. Onde aparece
- RCA (
POST /issues/{id}/diagnose): o contexto ganhaorchestrator_runs— runs da conexão do dataset nas 24 h antes da issue (até 5, mais recentes primeiro), comminutes_before_issuee a frasesummary. Um run falhado é a primeira hipótese do template offline («Job de orquestração falhado a montante»). - API:
GET /orchestrator-runs?connection_id=&source=&status=(sourcedbt_cloud,airflow,fivetran,airbyteoudbt_core) eGET /datasets/{id}/pipeline-health(§3).
Arquitetura: docs/ARQUITETURA.md §3.32 e §3.49.
6. Ferramentas de BI — Power BI, Tableau e Looker
O BeryX lê — só leitura, pelas APIs oficiais — o que a ferramenta de BI sabe dos seus datasets BI (modelo semântico do Power BI, fonte de dados publicada do Tableau, modelo LookML do Looker) e dos dashboards que os usam, e junta-lhe o que já sabe das tabelas:
- issues
bi_refresh(failed/delayed), um por dataset BI e subtipo, preso ao dataset do BeryX de que ele depende: «Refresh Tableau falhado: Vendas», com os dashboards que ficam com dados desatualizados. Resolvem-se sozinhos quando o refresh volta ao normal e ficam calados se marcados «ignorado». Um refresh agendado está atrasado quando o último sucesso tem mais do que o maior intervalo da agenda- 1 h; sem agenda (ligação viva, refresh manual) nunca está atrasado;
- linhagem tabela → dataset BI → dashboard: cada dashboard ligado a pelo menos um dataset do BeryX entra na linhagem como exposure, ao lado dos exposures dbt. O impacto de uma issue, o RCA, o grafo do conhecimento e os consumidores do data product passam a citar o dashboard e o seu dono;
- aviso de dashboards sem visualizações há N dias (
unused_after_days, 30 por omissão), no retrato da integração. Só com uso conhecido: sem permissão para ler a atividade, nenhum dashboard é dado como «sem uso». No Power BI, N vai de 7 a 27 e é 27 por omissão: a API de atividade só serve os últimos 28 dias e o dia de hoje ainda vai a meio, por isso cobre 27 dias completos. Um N acima disso dá 400.
6.1 Credenciais (só leitura)
| Ferramenta | Credenciais | base_url |
scope_id |
|---|---|---|---|
| Power BI | tenant_id, client_id, client_secret de um service principal do Entra ID com acesso de leitura aos workspaces; para o uso, a definição de inquilino que deixa service principals usar as APIs de administração só de leitura |
— (hosts fixos) | workspace do Power BI (opcional) |
| Tableau | token_name, token_secret de um personal access token |
servidor (Tableau Cloud ou Server) | site (contentUrl) |
| Looker | client_id, client_secret das API keys de um utilizador com leitura |
instância Looker | — |
Ficam cifradas (Fernet) e nunca voltam na API. Mudar o URL ou o âmbito obriga a reenviar as credenciais no mesmo pedido.
6.2 Configurar (admin)
curl -X POST $API/integrations/bi/tableau -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" -d '{
"name": "Tableau Cloud",
"base_url": "https://10ax.online.tableau.com",
"scope_id": "vendas",
"credentials": {"token_name": "datavault", "token_secret": "…"},
"warehouse_connection_id": "<conexão do armazém>",
"unused_after_days": 30
}'
curl -X POST $API/integrations/bi/tableau/<id>/test -H "Authorization: Bearer $TOKEN"
curl -X POST $API/integrations/bi/tableau/<id>/sync -H "Authorization: Bearer $TOKEN"
As tabelas que a ferramenta lê casam com os datasets da conexão do armazém
(warehouse_connection_id; sem ela, com os do workspace, só quando o nome é único),
por schema + tabela sem distinguir maiúsculas. O Power BI não diz as tabelas de um
modelo semântico na API REST pública: liga-o à mão com
"asset_mappings": [{"asset_id": "<id do modelo>", "dataset_ids": ["<dataset>"]}].
O worker repete o sync de hora a hora; GET /integrations/bi mostra o retrato do
último (datasets BI com estado, dashboards com uso, avisos e o que ficou por ler).
6.3 Limites de um sync
Cada sync tem um orçamento de 600 pedidos, com retries limitados em 429/5xx.
- Tableau e Looker cabem no orçamento com os tetos de páginas (Tableau) e de detalhes, explores e donos (Looker: 300, 150 e 100). O Tableau cabe mesmo com todos os retries (189 pedidos no pior caso). O Looker gasta até 553 pedidos sem retries: com muitos 429/5xx pode passar os 600, e o corte segue a regra abaixo.
- Power BI gasta cerca de 2 + 3 × workspaces + 2 × modelos com refresh +
dashboards + 2 × (N + 1) pedidos de atividade. Sem
scope_id, uma conta com dezenas de workspaces passa os 600. - Orçamento esgotado a meio: o sync fica
partial(POST …/syncresponde 200 compartial_code: request_budget) e grava o que leu por inteiro. As issues e os dashboards na linhagem que ficaram por ler não mudam: nada é resolvido nem apagado. Um dashboard lido só ganha ligações, e a issue de um dataset BI que só perdeu as tabelas casadas fica como estava, porque as ligações podem ter vindo cortadas. O sync seguinte volta a parar no mesmo ponto enquanto a conta tiver esse tamanho; no Power BI, a nota sugere uma integração por workspace (scope_id). - Resultados parciais do Tableau: os avisos
NODE_LIMIT_EXCEEDED,LINKED_RESULTS_INCOMPLETEeMAX_PAGE_SIZE_EXCEEDEDda Metadata API trazem dados parciais. O sync ficapartial(partial_code: partial_results), com a mesma regra. - Refreshes por ler (tarefas do Tableau ou datagroups do Looker sem permissão): o
dataset BI fica
unknowne a sua issue de refresh fica como estava. - Uso do Tableau: lê-se até 20 páginas de 1000 vistas. As vistas não vêm por livro, e o aviso compara totais entre syncs. Num site com mais de 20 000 vistas, ou com um erro ou o orçamento a meio das páginas, o uso de todos os livros fica desconhecido e nenhum sai no aviso.
Arquitetura: docs/ARQUITETURA.md §3.59.