Ir para o conteúdo

Integrações

Escolhendo um Caminho de Conexão

Tipo de cliente Caminho recomendado Por quê
Ferramentas de BI (Tableau, Power BI, Looker) JDBC Streaming colunar Arrow Flight pelo fio; ferramentas de BI têm um assistente JDBC embutido e se beneficiam da entrega colunar de alta vazão para grandes conjuntos de resultados
psql, DBeaver, qualquer ferramenta compatível com PG pgwire (driver PG nativo) Padrão de fricção zero — nenhum driver personalizado necessário; use o que você já tem
Stack de dados Python (pandas, pyarrow) provisa-client ou ADBC bruto Batches Arrow em streaming; sem overhead de serialização de linha
Spark, DuckDB, pipelines de alta vazão Arrow Flight (ADBC) Streaming colunar ilimitado direto para memória Arrow
Serviço a serviço (contratos tipados) Protobuf gRPC Proto gerado por função; linhas em streaming; segurança de tipo
Aplicações web, scripting HTTP (/data/graphql, /data/sql) Sem driver; HTTP padrão; escolha completa de linguagem de consulta
Clientes REST (padrão JSON:API) GET /data/jsonapi/{table} Envelope JSON:API v1.0; sparse fieldsets, paginação, filtragem via parâmetros de consulta; sem driver

pgwire — Driver PostgreSQL Nativo

O Provisa implementa o protocolo de fio PostgreSQL (versão de protocolo 3.0). Qualquer cliente que fale PostgreSQL se conecta sem um driver personalizado.

Habilite definindo PROVISA_PGWIRE_PORT (ex.: 5433) antes de iniciar o Provisa. Desabilitado quando não definido ou 0.

Por que pgwire em vez de JDBC?

O driver JDBC usa Arrow Flight como seu transporte e exige implantar o provisa-jdbc.jar. O pgwire não exige nada — se você já tem psql, DBeaver, SQLAlchemy, ou um driver JDBC PG, você já está pronto. É o caminho de menor fricção para cargas de trabalho somente SQL.

JDBC é a escolha certa para ferramentas de BI que têm um assistente de conexão JDBC embutido e se beneficiam do streaming colunar do Arrow Flight para grandes conjuntos de resultados. O pgwire aceita SQL livre contra o esquema publicado completo — as mesmas consultas, custo de configuração menor.

psql

psql -h localhost -p 5433 -U alice

DBeaver

  1. New Connection → PostgreSQL
  2. Host: localhost, Port: 5433
  3. Usuário / senha conforme configurado no Provisa
  4. Nenhum download de driver extra necessário

SQLAlchemy (Python)

from sqlalchemy import create_engine

engine = create_engine("postgresql+psycopg2://alice:secret@localhost:5433/provisa")
df = pd.read_sql("SELECT * FROM sales.orders", engine)

Ou com asyncpg:

engine = create_engine("postgresql+asyncpg://alice:secret@localhost:5433/provisa")

Autenticação

O campo password do pacote de inicialização carrega a credencial, e o que a credencial é determina o método: um token de acesso pessoal, um token bearer OIDC ou uma senha contra o provedor configurado. Sob o provedor basic com auth.scram: true, a senha é provada por SCRAM-SHA-256 em vez de enviada. Certificados de cliente são suportados. No modo trust (none), o nome de usuário mapeia diretamente para uma função e a senha é ignorada.

A tabela completa de interface × método está no Modelo de segurança. MD5 não é suportado; habilite TLS (PROVISA_PGWIRE_CERT / PROVISA_PGWIRE_KEY) ao rodar sobre uma rede não confiável.

Limitações

  • Somente SQL. GraphQL e Cypher não são aceitos via pgwire.
  • Não é somente leitura. As linhas das tabelas registradas são gravadas com INSERT, UPDATE, DELETE, MERGE, TRUNCATE e COPY ... FROM STDIN, cada uma admitida pelo direito write da função, suas concessões de colunas e seu filtro de linhas; veja Gravações de dados.
  • Sem definições: CREATE, ALTER e DROP são recusados com SQLSTATE 0A000, exceto a tabela temporária própria de uma sessão; veja Definições. COPY ... TO STDOUT exporta em text, csv e binary; COPY ... FROM STDIN lê text e csv.
  • Consultas a information_schema e pg_catalog são interceptadas e respondidas a partir de um shim de catálogo DuckDB — ferramentas de descoberta de esquema funcionam corretamente.

Driver JDBC

O driver JDBC do Provisa usa Arrow Flight como seu transporte subjacente. É o caminho recomendado para ferramentas de BI com um assistente de conexão JDBC.

As consultas passam pelo Arrow Flight sempre que a porta Flight responde quando a conexão é aberta, e os resultados chegam um lote por vez com seus tipos de coluna. A porta Flight é considerada a que fica ao lado da porta HTTP (8815 ao lado de 8001); defina a propriedade flight_port quando não for assim. Se a porta Flight não estiver acessível ao conectar, a conexão usa a API HTTP para suas consultas e registra isso em INFO. Uma vez que a conexão está no Flight, um erro do servidor — uma credencial recusada, uma função que o usuário não tem, uma instrução que falhou — é repassado ao chamador com a mensagem do servidor; não é tentado de novo por HTTP. (REQ-293)

A navegação no esquema (getTables, getColumns, chaves primárias e estrangeiras) é lida da mesma porta Flight, com a credencial de login: o driver lista o catálogo da função com que age, então uma tabela ou coluna não concedida a essa função não aparece. Uma chave que referencia uma tabela ou coluna não concedida à função também não é listada. Em uma conexão cuja porta Flight não estava acessível ao conectar, o driver lê o mesmo catálogo por HTTP (GET /data/catalog): o servidor monta as duas listagens com uma única função, então tabelas, nomes de coluna, tipos e chaves não dependem da porta que respondeu. (REQ-128)

Conexão

Baixe provisa-jdbc.jar (sempre a versão mais recente) e adicione-o ao caminho de driver da sua ferramenta.

URL JDBC:

jdbc:provisa://<host>:8815

A autenticação usa as propriedades JDBC padrão user / password. O driver as troca por um token de sessão e o envia em cada requisição; um login recusado faz a conexão falhar com o status e o motivo do servidor. O Provisa deriva a função da identidade que fez login. A propriedade opcional role (ou ?role= na URL) pede para agir como uma função que o usuário tem, ou um conjunto delas separado por vírgulas; uma função que o usuário não tem é recusada. Em uma implantação sem provedor de autenticação não há login, e o nome de usuário é tomado como a função solicitada. (REQ-131, REQ-273)

Configuração de Ferramenta de BI

Tableau

  1. Manage → Drivers → Install Provisa JDBC
  2. Connect → Other Databases (JDBC)
  3. URL: jdbc:provisa://localhost:8815
  4. Digite seu usuário e senha quando solicitado

DBeaver (caminho JDBC — para o caminho pgwire veja acima)

  1. Database → New Connection → JDBC
  2. Driver: adicione provisa-jdbc.jar
  3. URL: jdbc:provisa://localhost:8815
  4. Digite seu usuário e senha na aba Authentication

Power BI — use o gateway ODBC com a ponte Provisa JDBC-ODBC (incluída no instalador).


Clientes Arrow Flight

O Arrow Flight (porta 8815) é o caminho recomendado para ferramentas de dados que o suportam. Os resultados fazem streaming como Arrow RecordBatches sem materializar na memória do Provisa.

Python (provisa-client)

O caminho Python recomendado — envolve tanto GraphQL quanto Arrow Flight:

pip install provisa-client
from provisa_client import ProvisaClient

client = ProvisaClient("http://localhost:8001", username="alice", password="secret")

# Arrow Flight → pyarrow Table (high-throughput, streaming)
table = client.flight("SELECT id, amount FROM sales.orders")

# Arrow Flight → pandas DataFrame
df = client.flight_df("SELECT id, amount FROM sales.orders")

# GraphQL → DataFrame
df = client.query_df("{ orders { id amount } }")

Veja docs/python-client.md para a referência completa incluindo DB-API 2.0, dialeto SQLAlchemy, e ADBC.

Python (PyArrow bruto)

import pyarrow.flight as flight

client = flight.connect("grpc://localhost:8815")
ticket = flight.Ticket(b'{"query": "SELECT id, amount FROM sales.orders"}')
df = client.do_get(ticket).read_all().to_pandas()

O Flight carrega sua credencial no payload JSON, como um campo token — um token bearer do provedor ou um token de acesso pessoal. Tanto o handshake quanto cada ticket o aceitam, e ambos o validam do mesmo modo, então um cliente que se autenticou no handshake ainda apresenta o token em cada do_get. Um campo role ao lado solicita uma função; o servidor deriva as funções permitidas da identidade e substitui pelo valor autorizado, de modo que uma string de função em um ticket nunca é a identidade. (REQ-1263) Veja Modelo de segurança.

ticket = flight.Ticket(json.dumps({
    "query": "SELECT id, amount FROM sales.orders",
    "token": "provisa_pat_...",
    "role": "analyst",
}).encode())

A navegação também precisa da credencial. list_flights, get_flight_info e get_schema não levam ticket, então leem a credencial do cabeçalho de chamada authorization: Bearer <token> e a função solicitada de x-provisa-role (uma função, ou um conjunto de funções detidas separado por vírgulas). Com a autenticação ativada, uma chamada de navegação sem credencial é recusada, e o catálogo que ela retorna é o da função autorizada: as tabelas, colunas e comandos concedidos a essa função, e nada mais. Um ticket de catálogo (um ticket sem query) é respondido da mesma forma a partir do seu próprio token e role. Um cliente precisa, portanto, enviar sua credencial antes de navegar; o provisa-client faz isso em list_tables(). Em uma implantação sem provedor de autenticação essas chamadas listam o catálogo inteiro e não pedem credencial. (REQ-1263, REQ-127)

options = flight.FlightCallOptions(headers=[
    (b"authorization", b"Bearer provisa_pat_..."),
    (b"x-provisa-role", b"analyst"),
])
for info in client.list_flights(b"", options):
    print(info.descriptor.path)

ADBC

import adbc_driver_flightsql.dbapi as adbc

conn = adbc.connect("grpc://localhost:8815", db_kwargs={"username": "alice", "password": "secret"})
cursor = conn.cursor()
cursor.execute("SELECT id, amount FROM sales.orders")
table = cursor.fetch_arrow_table()

DuckDB

import duckdb, pyarrow.flight as flight

client = flight.connect("grpc://localhost:8815")
ticket = flight.Ticket(b'{"query": "SELECT * FROM sales.orders"}')
arrow_table = client.do_get(ticket).read_all()

conn = duckdb.connect()
result = conn.execute("SELECT region, sum(amount) FROM arrow_table GROUP BY 1").df()

Spark (PySpark)

from pyspark.sql import SparkSession
spark = SparkSession.builder \
    .config("spark.jars.packages", "org.apache.arrow:flight-core:14.0.0") \
    .getOrCreate()

# Use ADBC Flight connector or load via pandas → Spark

Protobuf gRPC (porta 50051)

Caminho de serviço para serviço. O Provisa gera um .proto por função e o regenera sempre que o modelo ou a função muda — cada função vê apenas as tabelas e colunas às quais tem acesso. O serviço tem uma RPC para cada tabela na raiz do esquema da função; as tabelas relacionadas são alcançadas pelos seus relacionamentos, como no GraphQL.

Baixe o proto para sua função:

curl http://localhost:8001/data/proto/analyst > provisa_analyst.proto

Use grpc_server_reflection para descobrir o esquema programaticamente. A reflexão descreve o serviço da função com que a chamada atua, o mesmo arquivo do download acima. Com a autenticação desativada, uma chamada de reflexão informa sua função como qualquer outra chamada:

grpcurl -plaintext -H 'x-provisa-role: analyst' localhost:50051 list

Todo RPC deve carregar uma credencial na chave de metadados authorization — um token do provedor ou um token de acesso pessoal. x-provisa-role solicita uma função do conjunto permitido da identidade; não é uma credencial e nunca foi. Certificados de cliente são suportados. Veja Modelo de segurança.

Consultas em streaming emitem uma mensagem por linha; mutações são unárias.


Invocando Comandos Entre Protocolos

Um comando é uma função rastreada registrada ou webhook — um chamável registrado no modelo do Provisa com um kind (query ou mutation) e um impl_kind que descreve como ele roda. Toda superfície roteia invocações através de um único executor governado (invoke_tracked_function) que aplica a atribuição do comando (visible_to), o direito write para uma mutação e governança uniformemente (REQ-1156). [tool-verified: provisa/api/data/action_exec.py, provisa/bolt/session.py:786-791, provisa/grpc/server.py:107-135, provisa/pgwire/function_call.py:80-88, provisa/api/flight/server.py:542-554]

impl_kind O que roda Campos de vinculação
source_procedure Procedimento armazenado em uma fonte registrada (padrão) sourceId, schemaName, functionName
script Script no lado do servidor script
http Chamada HTTP de saída url, method
grpc Chamada gRPC de saída para um servidor externo target, method
python Chamável Python hospedado pelo Provisa (REQ-885) callable (ex.: demo.py_functions:random_dataset)

Quando um comando declara um return_schema (JSON Schema com type: array, items: object), ele retorna conjunto — toda superfície o projeta como um conjunto de linhas tipado. Os comandos de demonstração random_python_set (impl_kind python) e random_grpc_set (impl_kind grpc) ilustram tanto um chamável hospedado quanto uma ponte gRPC externa retornando linhas com valores aleatórios; ambos são registrados em config/provisa-install.yaml. [tool-verified: config/provisa-install.yaml:809-856]

Matriz de protocolo

Superfície Sintaxe Exemplo
GraphQL kind=query → campo Query; kind=mutation → campo Mutation; prefixado por domínio quando domain_prefix: true { ps__random_python_set(rows: 5, seed: 42) { id region amount } }
pgwire / Arrow Flight / MCP run_sql SELECT * FROM fn(args) ou SELECT fn(args) SELECT * FROM random_python_set(5, 42)
Cypher HTTP (POST /data/cypher) CALL fn(args) YIELD cols CALL random_python_set(5, 42) YIELD id, region, amount
Bolt (Neo4j Browser / driver) CALL fn(args) — argumentos posicionais mapeiam para nomes de argumento declarados CALL random_python_set(3, 7)
Provisa gRPC (porta 50051) Unário CallCommand(CommandRequest{name, args_json}) → CommandResponse{rows_json} grpcurl -d '{"name":"random_python_set","args_json":"{\"rows\":5}"}' ... ProvisaService/CallCommand

O campo kind controla apenas o posicionamento no GraphQL — as superfícies SQL, Cypher, Bolt, e gRPC aceitam comandos query e mutation de forma idêntica.


Apollo Federation

O Provisa pode atuar como um subgraph Federation v2, expondo seu esquema publicado a um Apollo Router ou Apollo Gateway.

Configuração

Habilite federation em config.yaml:

federation:
  enabled: true
  subgraph_name: provisa-data

O Provisa gera diretivas @key em colunas de chave primária e @external/@provides em relacionamentos entre subgraphs automaticamente.

Registro com Apollo Router

No seu supergraph.yaml:

subgraphs:
  provisa-data:
    routing_url: http://provisa:8001/data/graphql
    schema:
      subgraph_url: http://provisa:8001/data/graphql

Rode rover supergraph compose --config supergraph.yaml para gerar o esquema do supergraph.

Entidades

O Provisa responde a consultas _entities para joins entre subgraphs. Qualquer tabela com uma chave primária é automaticamente resolvível como uma entidade Federation.


Importação Hasura v2 / DDN

Veja docs/import.md para migração do Hasura para o Provisa.


Kafka

Veja docs/sources.md para configuração de tópico Kafka como tabelas somente leitura e sinks de resultado de consulta.


Verificadores de Qualidade de Dados (REQ-1443)

Soda Core e Great Expectations se conectam ao Provisa da mesma forma que qualquer outro cliente postgres — através do pgwire. Essa é toda a integração: o verificador mantém um driver postgres e varre a exibição federada, de modo que uma tabela Snowflake, uma tabela Iceberg e uma coleção Mongo são todas verificadas pelo mesmo dialeto de contrato, sem verificador por sistema. [tool-verified: provisa/events/source_loader.py make_dq_loader]

A varredura roda em um interpretador filho — python -m provisa.dq.worker — que é o único lugar onde soda_core ou great_expectations é importado. Nada é vinculado ao processo do servidor, e uma falha do verificador derruba um subprocesso em vez do loop de eventos. [tool-verified: provisa/dq/runner.py build_command]

Os resultados da varredura chegam como linhas de fonte comuns, então cadência, atualidade, eventos, linhagem, governança, RLS, a grade e exportação se aplicam todos sem um segundo mecanismo. A autoria de contrato, o envelope de resultado e o registro derivado são cobertos em docs/sources.md.

Instalando um verificador

Nenhuma das bibliotecas vem instalada por padrão. O instalador pergunta qual você quer, e a resposta se torna dq_checker: none|soda|gx em ~/.provisa/config.yaml. No nível Docker, scripts/provisa transforma isso no argumento de build PROVISA_EXTRAS; no nível nativo, first-launch.sh instala o extra pyproject correspondente no venv. [tool-verified: scripts/provisa:69-79, packaging/linux/first-launch.sh _native_extras]

dq_checker Biblioteca Licença Plano cloud hospedado
soda soda-postgres Elastic License 2.0 Recusado (cloud_eligible: false)
gx great-expectations[postgresql] Apache 2.0 Permitido

O Elastic License 2.0 proíbe fornecer o software a terceiros como um serviço hospedado, que é o que rodar o Soda dentro do plano SaaS em nome de um locatário seria. Uma implantação hospedada que deseja Soda aponta para um endpoint Soda que o operador roda por conta própria. Veja docs/configuration.md para as chaves de conexão.


Intercâmbio semântico Apache Ossie (REQ-1316)

O Provisa troca modelos semânticos com o Apache Ossie (especificação 0.2.0.dev0, em incubação; antes Open Semantic Interchange) por meio de um adaptador de fronteira. O vocabulário interno do Provisa nunca é renomeado para o do Ossie — a especificação declara prováveis mudanças incompatíveis, então o acoplamento fica restrito ao adaptador. [tool-verified: provisa/ossie/convert.py module docstring; OSSIE_VERSION = "0.2.0.dev0"]

A especificação em rascunho mudou de forma sem mudar sua string de versão. O adaptador segue a especificação como está no commit 642cb894 do apache/ossie (2026-10-10): um modelo por documento, com version, name, datasets, relationships e metrics na raiz do documento, e um datatype enumerado. A exportação é verificada nos testes unitários contra o core-spec/ossie-schema.json desse commit. [tool-verified: OSSIE_SPEC_COMMIT, provisa/ossie/convert.py; test_the_export_is_valid_against_ossies_own_schema, tests/unit/test_ossie_convert.py]

Exportação

A superfície canônica de exportação é um endpoint HTTP ativo. Ele deriva o documento Ossie do estado ativo a cada requisição — sem cache, sem etapa de geração.

GET /admin/ossie
A resposta é um documento YAML com Content-Disposition: attachment; filename=provisa.ossie.yaml. [tool-verified: ossie_router.py lines 20–33: "THE canonical live Ossie endpoint: the semantic model derived from live state on every read — no caching, no regeneration step"]

A página Métricas também oferece um botão Baixar e uma URL de endpoint copiável no painel Ossie Interchange, ambos apontando para o mesmo endpoint. [tool-verified: OssieInterchangePanel.tsx lines 64–79: endpointUrl = window.location.origin + OSSIE_ENDPOINT_PATH]

O que é exportado

O adaptador mapeia objetos do Provisa para objetos do Ossie assim:

Objeto do Provisa Objeto do Ossie Notas
Table dataset source = catalog.schema.table; chaves primárias/únicas a partir da configuração das colunas e de UniqueConstraint
Column field expression = referência de coluna (dialeto ANSI_SQL); colunas de tempo recebem dimension.is_time: true
Relationship relationship O alias é usado como nome quando definido; relacionamentos calculados (com destino função) são ignorados
Metric metric name, expression (ANSI_SQL), datatype (mapeado como o de uma coluna), description, ai_context
modeling_role / modeling_history custom_extensions[].vendor_name="provisa" Apenas ida e volta; outras ferramentas podem ignorar

[tool-verified: _table_to_dataset, build_ossie_model, provisa/ossie/convert.py: "Computed (function-target) relationships have no dataset target — not representable in Ossie; skipping is the defined export boundary"]

Um relacionamento sobre várias colunas lista cada coluna em from_columns e to_columns. Um modelo sem nenhuma tabela é recusado com 422: um documento Ossie tem pelo menos um dataset. [tool-verified: test_a_relationship_over_several_columns_lists_each_column, test_a_model_with_no_table_is_refused_by_name, tests/unit/test_ossie_convert.py]

Governança, RLS, linhagem e semântica de grafos não são exportadas. Elas podem viajar no slot opcional provisa de custom_extensions para a fidelidade da ida e volta, mas o intercâmbio nunca depende de outras ferramentas o lerem. [tool-verified: provisa/ossie/convert.py docstring lines 13–15]

Um tipo de coluna fora do vocabulário do Ossie é escrito como Opaque, com o nome próprio do tipo na extensão provisa do campo, que é o que a especificação manda para um tipo conhecido para o qual não tem membro. Uma coluna sem tipo registrado não informa datatype. O adaptador nunca mapeia para um tipo que a coluna não tem. [tool-verified: _map_datatype, _typed, provisa/ossie/convert.py]

Mapeamento de tipos

[tool-verified: _DATATYPE_MAP, provisa/ossie/convert.py]

Tipo do Provisa / da fonte datatype do Ossie
varchar, text, char, uuid, string String
int, integer, bigint, smallint, int4, int8, tinyint Integer
numeric, decimal Decimal
float, double, real Float
bool, boolean Boolean
date Date
time Time
timestamp, datetime DateTime
timestamptz, timestamp with time zone DateTimeTz
qualquer outro Opaque, com o nome do tipo na extensão provisa

O Decimal do Ossie não traz precisão nem escala, então numeric(10,2) é exportado como Decimal e volta de uma importação como decimal. [tool-verified: test_parse_round_trips_export, tests/unit/test_ossie_convert.py]

Importação

A importação aceita um documento Ossie (YAML ou JSON) e retorna propostas de registro. Nada é registrado automaticamente — definições importadas nunca pulam a etapa de revisão.

POST /admin/ossie/import
Content-Type: text/yaml   (or application/json)

<ossie document>
O servidor analisa o documento com parse_ossie_model, que valida a estrutura e retorna uma dataclass OssieImport contendo as tabelas, os relacionamentos e as métricas propostos como dicionários simples. Qualquer problema estrutural é um 400 com um erro nomeado pelo caminho, por exemplo ossie import: missing $.datasets[1].source. [tool-verified: import_ossie, provisa/api/admin/ossie_router.py: "Nothing is registered here — imported definitions never bypass registration review"]

O importador lê apenas a forma plana do documento. Três coisas são recusadas pelo nome:

  • Um documento que envolve seu modelo em semantic_model, a forma que a especificação em rascunho tinha antes de setembro de 2026. O erro diz que o documento é anterior à forma plana e como convertê-lo.
  • Uma version diferente de 0.2.0.dev0.
  • Um datatype que não é membro do vocabulário do Ossie; o erro nomeia o caminho e lista os membros.

Uma coluna ou métrica proposta traz o tipo que o seu datatype Ossie representa (Integer propõe bigint, DateTime propõe timestamp). Opaque propõe o tipo que a extensão provisa nomeia quando o documento veio do Provisa, e nenhum tipo caso contrário. Um ai_context dado como objeto é lido pelas suas instructions.

Um campo só é proposto como coluna quando sua expressão é uma coluna da fonte. Um campo cuja expressão é um cálculo não é proposto; a tela de revisão o nomeia sob sua tabela como não importado. Um campo que renomeia sua coluna é proposto como essa coluna, com o nome do campo como alias da coluna. A source de um dataset deve ter três partes, source.schema.table; uma consulta ou um nome mais curto é recusado, e o erro diz isso. [tool-verified: parse_ossie_model, _parse_dataset, _proposed_type, _ai_context, provisa/ossie/convert.py; tests/unit/test_ossie_convert.py]

Vindo do dbt, Cube, Snowflake e outras camadas semânticas

O projeto Apache Ossie mantém conversores entre o Ossie e os formatos de modelo de outras ferramentas. Execute o conversor da sua ferramenta e depois envie sua saída ao endpoint de importação ou escolha o arquivo na tela de revisão abaixo. O Provisa lê o documento Ossie; ele não lê os arquivos da outra ferramenta.

Origem Comando do conversor O que ele lê
dbt (camada semântica) ossie-dbt msi-to-ossie -i target/semantic_manifest.json -o semantic_model.yaml O semantic_manifest.json que o dbt escreve para o MetricFlow
Cube ossie-cube import -i model/ -o model.yaml Um diretório de modelo do Cube, ou arquivos individuais de cubos e views

[tool-verified: converters/dbt/README.md and converters/cube/README.md in the apache/ossie repository, read 2026-10-10]

O mesmo repositório tem conversores para Snowflake, Databricks, Microsoft, Salesforce, ThoughtSpot, Sigma, Omni, Hex, GoodData e outros; cada um tem seu próprio README. [tool-verified: directory listing of converters/ in apache/ossie, 2026-10-10]

Dois limites a conhecer antes de converter:

  • O conversor do dbt lê apenas o manifesto da camada semântica. Um projeto dbt sem modelos semânticos não tem nada para ele converter. Ele descarta métricas de conversão, métricas privadas e entidades de chave natural, e imprime um aviso para cada uma. [tool-verified: converters/dbt/README.md, the "MSI → Ossie is lossy" table]
  • A saída de um conversor segue a especificação como o próprio commit desse conversor a tem. O Provisa lê a forma plana do commit 642cb894 do apache/ossie; um conversor anterior a setembro de 2026 escreve o invólucro semantic_model, que o Provisa recusa pelo nome.

Os dois caminhos foram executados em 2026-10-10 com os conversores instalados a partir do commit 642cb894 do apache/ossie (eles não são publicados como pacotes; instalam-se a partir dos diretórios converters/dbt e converters/cube do repositório e precisam de Python 3.11 ou posterior). Um modelo de duas tabelas, um relacionamento e uma métrica foi exportado do Provisa, convertido para um manifesto semântico do dbt e para um modelo Cube, convertido de volta para Ossie por ossie-dbt msi-to-ossie e ossie-cube import, e lido por parse_ossie_model:

Caminho Resultado
Modelo Cube → Ossie → Provisa Lido. Tabelas, chaves, tipos de coluna, o relacionamento e a métrica voltaram; o nome do relacionamento era o do conversor (orders_to_customers).
Manifesto semântico do dbt → Ossie → Provisa Lido. Tabelas, chaves primárias e a métrica voltaram. Os tipos de coluna não (a saída do conversor não informa nenhum), e o relacionamento voltou como o conversor o derivou das entidades do dbt, que não era o registrado. Revise os relacionamentos antes de aplicar.

[tool-verified: both runs, 2026-10-10, apache/ossie commit 642cb894, ossie-dbt and ossie-cube 0.2.0.dev0, metricflow 0.213.0; inputs were files those converters had written from a Provisa export, not files authored in dbt or Cube]

O conversor do dbt recusa um modelo em que alguma tabela tem uma restrição de unicidade sobre várias colunas ("MetricFlow entities cannot represent composite keys losslessly"). [tool-verified: ossie-dbt ossie-to-msi on the same export with such a constraint, 2026-10-10]

A tela de revisão

Na interface, o botão Importar (página Métricas → painel Ossie Interchange) abre um seletor de arquivos. Depois que o documento é enviado e analisado, abre-se uma janela de revisão com cada tabela, relacionamento e métrica proposta listada como item marcado. O modelador pode desmarcar qualquer coisa para excluí-la. Clicar em Aplicar registra os itens marcados pelas mutações de registro existentes — primeiro as tabelas, depois os relacionamentos (que referenciam tabelas), depois as métricas. [tool-verified: OssieInterchangePanel.tsx lines 88–165: "Review screen opens with everything checked; trimming = unchecking"; "Tables first, then relationships... then metrics — each through the EXISTING registration mutations (REQ-1316)"]

A função de modelagem e o histórico guardados em um documento Ossie exportado pelo Provisa fazem a ida e volta corretamente na importação. [tool-verified: _parse_dataset custom_extensions handling, provisa/ossie/convert.py lines 287–300: "REQ-1320: round-trip the provisa modeling metadata slot"]

Arquivo de sources do dbt (REQ-1967)

Para um projeto dbt que não usa a camada semântica, o Provisa escreve diretamente um sources.yml do dbt. Ele é derivado do modelo ativo a cada requisição.

GET /admin/dbt/sources?role=<role id>
A resposta é YAML com Content-Disposition: attachment; filename=provisa.sources.yml. O chamador precisa de org_settings, e role deve ser uma função que o chamador tem; o detentor de access_config pode nomear qualquer função. O painel de intercâmbio da página Métricas oferece o mesmo download para a função em uso. [tool-verified: download_dbt_sources, provisa/api/admin/ossie_router.py; TestTheEndpoint, tests/unit/test_dbt_sources.py]

O arquivo lista cada tabela servida à função, sob uma source do dbt por fonte e esquema (nomeada pelo id da fonte, ou <source>_<schema> quando uma fonte tem tabelas em mais de um esquema), com a descrição da tabela, as descrições das colunas servidas e estes testes:

O modelo informa O arquivo escreve
Uma chave primária de uma coluna unique e not_null nessa coluna
Uma chave primária de várias colunas not_null em cada coluna
Uma restrição de unicidade de uma coluna unique nessa coluna
Um relacionamento entre duas tabelas ambas servidas à função relationships na coluna de origem, para source('<source>', '<table>')

Uma coluna não servida à função fica ausente, assim como uma tabela sem nenhuma coluna servida. Uma chave sobre várias colunas não tem teste padrão do dbt; o comentário inicial do arquivo nomeia cada uma, e nenhum teste é escrito para ela. O modelo não registra a nulabilidade, então not_null aparece só em colunas de chave. O arquivo não traz métricas, segurança de linha, mascaramento, funções nem linhagem, e seu comentário inicial diz isso. [tool-verified: build_dbt_sources, provisa/dbt/sources.py; tests/unit/test_dbt_sources.py]

Um arquivo gerado a partir de um modelo de duas tabelas e um relacionamento foi analisado pelo dbt-core 1.12.5 com dbt-duckdb 1.11.0: duas sources e cinco testes, sem avisos. O dbt-core 1.9.11 não lê seus testes relationships, que aninham seus argumentos sob arguments; versões intermediárias não foram testadas. [tool-verified: dbt parse and dbt ls on both versions, 2026-10-10; dbt test against data was not run]


Métricas Entre Protocolos (REQ-1319)

A definição de uma métrica governada — sua expressão, descrição, e ai_context — viaja com o valor para toda superfície de consulta através de uma única expansão do compilador. Não há cópias. O compilador reserva o esquema metrics para acesso SQL; cada protocolo então adiciona seu próprio canal de metadados.

[tool-verified: METRICS_SCHEMA = "metrics", provisa/compiler/metric_expand.py line 43; REQ-1319 requirement text: "the definition (description, ai_context) travels with the value everywhere, with no copies"]

SQL / pgwire

Endereça qualquer métrica como uma relação virtual no esquema metrics. As colunas de dimensão que você seleciona tornam-se o GROUP BY:

-- Grand total
SELECT value FROM metrics.net_revenue;

-- By region
SELECT region, value FROM metrics.net_revenue GROUP BY region;

-- By region and month, filtered
SELECT region, month, value
FROM metrics.net_revenue
WHERE net_revenue.status = 'completed'
GROUP BY region, month;

O compilador expande a forma metrics.<name> para o agregado agrupado real antes da governança rodar. Descrições de coluna são exibidas como entradas pg_description, então o DBeaver e o \d+ do psql as mostram. [tool-verified: metric_semantic_sql, provisa/compiler/metric_expand.py lines 52–70; REQ-1319: "description surfaced via pg_description"]

SELECT * é rejeitado — nomeie as colunas explicitamente. [tool-verified: expand_metric_query, provisa/compiler/metric_expand.py lines 302–306]

GraphQL

Métricas se projetam dentro do campo raiz _aggregate como um bloco metrics. [inferred: per REQ-1319; aggregate_gen.py not read in this session]

O texto da definição (description, ai_context) aparece na documentação de introspecção do GraphQL, então ferramentas cientes de esquema e geração de código o captam automaticamente. [inferred: per REQ-1319: "definition in introspection docs"]

MCP (agentes de IA)

Duas ferramentas expõem métricas a clientes MCP:

  • list_metrics — retorna todas as métricas governadas visíveis à sessão, com name, description, e ai_context.
  • query_metric — aceita um nome de métrica mais uma lista de dimensões e chama o caminho de SQL semântico do compilador, retornando o resultado agregado.

[inferred: per REQ-1319: "MCP: list_metrics and query_metric tools carrying ai_context, so agents select governed meanings instead of composing aggregation SQL"; provisa/api/mcp/tools.py not read in this session]

Agentes que chamam list_metrics antes de construir uma consulta selecionam uma métrica governada pelo nome em vez de escrever SQL de agregação manualmente. O campo ai_context é o lugar para colocar o texto de definição que orienta a seleção correta.

Arrow Flight

Métricas são endereçáveis como descritores de flight de métrica retornando tabelas Arrow. [inferred: per REQ-1319: "Arrow Flight: metric flight descriptors returning Arrow tables"; provisa/api/flight/catalog.py not read in this session]

Use a mesma forma SQL metrics.<name> via o caminho de ticket Flight SQL padrão.

Bolt / Cypher (Neo4j Browser)

Chame uma métrica usando o procedimento provisa.metric():

CALL provisa.metric('net_revenue', ['region']) YIELD region, value

[inferred: per REQ-1319: "Bolt/Cypher: a provisa.metric() procedure"; the procedure signature is inferred from the REQ text and not verified against provisa/bolt/session.py in this session]

Tabelas Fact e Dimension carregam rótulos de nó :Fact e :Dimension no grafo federado, então o Bloom renderiza a forma de estrela automaticamente. [inferred: per REQ-1319 and REQ-1320: "federated graph labels nodes :Fact/:Dimension so Bloom renders the star"; provisa/cypher/label_map.py not read in this session]

Consultas em linguagem natural

O matcher de esquema NL resolve vocabulário de métrica em perguntas em linguagem natural diretamente para uma métrica mais dimensões, depois gera SQL semântico. [tool-verified: resolve_metric, provisa/nl/schema_matcher.py is exercised in test_nl_metrics.py lines 76–78: sql = matcher.resolve_metric("What is the total revenue by region?") → "SELECT region, value FROM metrics.total_revenue GROUP BY region"]

Tabelas fact são marcadas [fact] no prompt NL; tabelas de dimensão são marcadas [dimension]. O matcher favorece caminhos de join fact-para-dimensão ao resolver perguntas. [tool-verified: test_format_entities_tags_star_roles, tests/unit/test_nl_metrics.py lines 129–132: assert "table: orders [fact] fields: amount" in block]

Streaming

Combine view_metrics com materialize para manter uma view de métricas atualizada conforme suas entradas mudam, usando a maquinaria de materialização existente. Nenhum pipeline novo é exigido. [inferred: per REQ-1319. The table Kafka sink that requirement named is retired (REQ-176); a table publishes its new rows to Kafka through a live output (live.outputs, REQ-565)]

Observabilidade (OTel)

Avaliações de métrica são rastreadas e exportáveis como métricas OpenTelemetry. [inferred: per REQ-1319: "Observability: metric evaluations traced and exportable as OTel metrics"; OTel integration code not read in this session]