Migrando do Hasura v2 para o Provisa¶
Pré-requisitos¶
- Uma instância Hasura v2 em execução (v2.x) com metadados exportados.
- Exporte os metadados usando a CLI do Hasura:
Isso cria um diretório metadata/ contendo sources.yaml, actions.yaml,
cron_triggers.yaml, inherited_roles.yaml, remote_schemas.yaml, etc.
3. Python 3.11+ com o pacote provisa instalado.
Uso da CLI¶
Argumentos¶
| Argumento | Obrigatório | Descrição |
|---|---|---|
metadata_dir |
Sim | Caminho para o diretório de metadados Hasura v2 exportado |
Opções¶
| Opção | Padrão | Descrição |
|---|---|---|
-o, --output FILE |
stdout | Caminho do arquivo YAML de saída |
--source-overrides FILE |
Nenhum | Arquivo YAML com overrides de conexão por fonte |
--domain-map KEY=VAL ... |
Nenhum | Mapeamentos de esquema-para-domínio (ex.: public=core hr=people) |
--auth-env-file FILE |
Nenhum | Caminho para um arquivo .env com config de auth JWT/admin-secret |
--dry-run |
desligado | Analisa e valida sem escrever saída |
Arquivo de Overrides de Fonte¶
Um arquivo YAML chaveado pelo nome da fonte com propriedades de conexão a sobrepor:
default:
host: prod-db.example.com
port: 5432
database: myapp
username: provisa_user
password: "${env:PROD_DB_PASSWORD}"
Arquivo de Ambiente de Auth¶
Um arquivo estilo .env contendo a config de auth do Hasura a converter. O conversor mapeia:
- JWT com
jwk_url->provider: oauthdo Provisa. claims_mapJWT ->role_mapping[]do Provisa.- Admin secret ->
superuserdo Provisa. - Auth via webhook -> aviso emitido (sem equivalente Provisa).
Matriz de Paridade de Recursos¶
| Recurso Hasura v2 | Equivalente Provisa | Notas |
|---|---|---|
| Sources (postgres, mysql, mssql, bigquery, citus) | sources[] |
Kind mapeado: pg/postgres -> postgresql, mssql -> sqlserver. URL de conexão analisada em host/porta/banco de dados/usuário/senha. Configurações de pool preservadas. |
| Tables (tabelas rastreadas) | tables[] |
Esquema + nome de tabela preservados. source_id liga à fonte. |
Nomes de tabela customizados (custom_name, custom_root_fields.select) |
tables[].alias |
Primeiro não-nulo de select, select_by_pk, custom_name. |
| Nomes de coluna customizados | columns[].alias |
Mapeia o dict custom_column_names para aliases de coluna. |
| Permissões de select (colunas, filter) | columns[].visible_to[], rls_rules[] |
Listas de coluna se tornam visible_to. Colunas curinga (*) suportadas. Filtros convertidos para SQL via bool_expr_to_sql. |
| Permissões de insert/update (colunas) | columns[].writable_by[] |
Listas de coluna se tornam writable_by. Funções elevadas à capacidade write. |
| Permissões de delete | Elevação de capacidade de função | A função recebe a capacidade write. Sem mapeamento de delete por tabela. |
| Relacionamentos object | relationships[] com cardinality: many-to-one |
Mapeamento de coluna preservado. |
| Relacionamentos array | relationships[] com cardinality: one-to-many |
Mapeamento de coluna preservado. |
| Computed fields | functions[] |
Mapeado para Function com returns apontando para o ID da tabela pai. |
| Tracked functions | functions[] |
exposed_as assume mutation por padrão. Esquema preservado. |
| Actions (handler de stored procedure) | functions[] |
Convertido para uma config Function quando suportado por uma stored procedure. |
| Actions (handler de webhook) | Não convertido | Aviso emitido, incluindo a URL do handler. |
| Cron triggers | Não convertido | Aviso emitido. (Triggers agendados de runtime existem, mas o conversor não os mapeia.) |
| Event triggers | Não convertido | Aviso emitido. (Triggers de evento de runtime existem, mas o conversor não os mapeia.) |
| Inherited roles | roles[].parent_role_id |
A primeira função em role_set se torna pai. Todas as funções filhas criadas. |
| Remote schemas | sources[] (graphql_remote) |
Registrado como uma fonte graphql_remote. Nome, URL, headers, e config de autenticação preservados. |
| Enum tables | Tabela criada | Flag is_enum não carregada (sem equivalente Provisa). |
| Allow lists | Ignorado | Não presente no modelo de metadados. |
Etapas Pós-Conversão¶
- Revise o YAML de saída. Verifique se fontes, tabelas, e funções parecem corretas.
- Configure conexões de fonte. O conversor analisa URLs de conexão mas assume
localhostcomo padrão em caso de falha de análise. Use--source-overridesou edite a saída diretamente. - Verifique as atribuições de domínio. Sem
--domain-map, todas as tabelas caem emdefault. Atribua esquemas a domínios com--domain-map public=core analytics=reporting. - Verifique as regras RLS. Filtros são convertidos para aproximações SQL. Expressões booleanas
complexas (
_and/_or/_existsaninhados) devem ser revisadas manualmente. - Revise os avisos. O conversor imprime um resumo de avisos no stderr para recursos que o conversor não mapeia (event triggers, cron triggers, actions suportadas por webhook).
- Configure a auth. Se sua instância Hasura usa auth JWT/webhook, crie um arquivo de ambiente
de auth e reexecute com
--auth-env-file. - Teste. Inicie o servidor Provisa e verifique consultas contra suas fontes de dados.
Problemas Comuns e Solução de Problemas¶
URL de conexão não analisada¶
Se o database_url da fonte é uma referência de variável de ambiente
({"from_env": "PG_URL"}), o conversor não consegue resolvê-la no momento da conversão. A fonte
terá valores placeholder (host: localhost, database: default). Corrija com
--source-overrides.
Colunas curinga¶
Quando uma permissão concede columns: "*", o conversor cria uma única entrada de coluna curinga.
Após a conversão, você pode querer substituí-la por listas de coluna explícitas inspecionando o
esquema real do banco de dados.
Fidelidade de event trigger¶
Event triggers são convertidos com operations e webhook_url mas garantias de entrega
específicas do Hasura (exactly-once, redelivery) não têm equivalentes Provisa diretos. Revise a
seção event_triggers e configure sua infraestrutura de webhook adequadamente.
Funções ausentes¶
Funções são coletadas somente de entradas de permissão. Se uma função existe no Hasura mas não tem permissões em nenhuma tabela ou action, ela não aparecerá na saída.
Campos raiz customizados¶
Somente os campos raiz select e select_by_pk são usados para o alias da tabela. Outros campos
raiz customizados (select_aggregate, insert, update, delete) não são mapeados.
Exemplo¶
Converta um projeto Hasura v2 típico com dois esquemas mapeados para domínios:
# Export metadata from Hasura
hasura metadata export --endpoint http://localhost:8080
# Convert with domain mapping and source overrides
python -m provisa.hasura_v2 metadata/ \
-o provisa.yaml \
--domain-map public=core hr=people \
--source-overrides overrides.yaml \
--auth-env-file auth.env
# Dry run first to check for warnings
python -m provisa.hasura_v2 metadata/ --dry-run
Estrutura de saída:
sources:
- id: default
type: postgresql
host: prod-db.example.com
port: 5432
database: myapp
...
domains:
- id: core
- id: people
tables:
- source_id: default
domain_id: core
schema_name: public
table_name: users
columns:
- name: id
visible_to: [user, admin]
- name: email
visible_to: [admin]
writable_by: [admin]
alias: Users
roles:
- id: admin
capabilities: [read, write]
domain_access: ["*"]
- id: user
capabilities: [read]
domain_access: ["*"]
rls_rules:
- table_id: default.public.users
role_id: user
filter: "id = x-hasura-user-id"
relationships:
- id: default.public.orders.user
source_table_id: default.public.orders
target_table_id: default.public.users
source_column: user_id
target_column: id
cardinality: many-to-one