Catálogo externo — publicar a qualidade no OpenMetadata, no DataHub, no Collibra e no Microsoft Purview (CATSYNC v1/v2/v3, cartão #398)
O BeryX publica, no fim de cada scan, o veredito, o score e o resultado de cada regra como testes de Data Quality no OpenMetadata do cliente. Quem vive no catálogo passa a ver a qualidade medida pelo BeryX na aba Data Quality / Profiler da própria tabela — sem abrir outra ferramenta.
Desde o CATSYNC v2 o mesmo push também publica no DataHub (assertions) — ver DataHub — e desde o CATSYNC v3 no Collibra (assets «Data Quality Metric» pela Import API) — ver Collibra. No Microsoft Purview (cartão #398) o veredito vai para atributos da própria tabela no Data Map — ver Microsoft Purview. Os catálogos partilham tudo exceto o cliente HTTP: mapeamento por conexão, conteúdo publicado, limites, retry, best-effort e API.
Read-only nas fontes mantém-se: o BeryX escreve só no catálogo que o admin ligou (opt-in por conexão) e só entidades próprias — no OpenMetadata, entidades de Data Quality com prefixo
datavault(definições, test cases e resultados); no DataHub, assertionsdatavault-*; no Collibra, assets «Data Quality Metric»DataVault:*; no Purview, os atributos personalizadosdatavault_*da entidade da tabela. Nunca altera tabelas, owners, tags, glossário nem lineage.
No sentido inverso, o BeryX também lê a curadoria do catálogo (domínios, owners, tags de sensibilidade e descrições) — ver Ler do catálogo. Essa leitura nunca escreve no catálogo.
O que aparece no OpenMetadata
Por cada dataset do scan com FQN mapeado e tabela existente no catálogo:
| Test case | Nível | Estado | Resultado |
|---|---|---|---|
datavault_quality |
tabela | Success se o veredito é pass/warn, Failed se fail, Aborted sem regras |
«Veredito BeryX AVISO · score 82.5 · 2 regra(s) falhada(s)» + valores verdict, score, failed_rules |
datavault_<tipo>_<id> |
coluna (ou tabela) | Failed / Success |
severidade, valor observado e limiar |
- O veredito é o mesmo do gate de CI (
GET /datasets/{id}/verdict):fail= regra crítica falhada ou contrato ODCS inválido;warn= só warnings.warnpublicaSuccessno test case do veredito (o catálogo não tem estado de aviso) — as regras em aviso aparecem comoFailednos seus test cases. - Regras que voltam a passar publicam
Success: o histórico no OpenMetadata mostra quando o problema apareceu e quando foi resolvido. - Limites por push: 200 datasets, 50 regras por dataset (falhadas primeiro).
- Só metadados e agregados (nomes de regras/colunas, contagens, min/max, score). Nunca linhas nem amostras.
Setup (10 minutos)
1. Criar o bot no OpenMetadata
- Settings → Bots → Add bot — ex.:
datavault-bot. - Atribui-lhe um papel que permita criar e editar
testDefinition,testCase(e respetivos resultados) e lertable— ex.: um papelDataVaultPublishercom uma política só com essas regras. Não precisa de mais nada: o BeryX não toca noutras entidades. - Copia o JWT token do bot.
2. Ligar o servidor no BeryX
Conexões → cartão da fonte → Catálogo externo → Ligar catálogo:
- URL do servidor:
https://openmetadata.empresa.pt(aceita também…/apiou…/api/v1). - Token do bot: o JWT do passo 1. É cifrado (Fernet) antes de gravar e nunca volta a ser mostrado nem aparece em logs.
- «Ligar e testar» confirma URL e token (
GET /api/v1/users/loggedInUser).
O URL passa pela proteção anti-SSRF: endereços privados/internos são
recusados. Um OpenMetadata na rede interna do cliente (ex.: BeryX
self-hosted no mesmo perímetro) exige o host em
DATAVAULT_CONNECTOR_ALLOWED_HOSTS.
3. Mapear os datasets para FQNs
No OpenMetadata o FQN de uma tabela é serviço.base.schema.tabela. Na
conexão indica o prefixo FQN — o serviço e a base desta fonte — e o
BeryX acrescenta schema.tabela:
| Fonte no BeryX | Prefixo FQN | Dataset | FQN publicado |
|---|---|---|---|
PostgreSQL analytics |
postgres_prod.analytics |
crm.clientes |
postgres_prod.analytics.crm.clientes |
Snowflake DWH |
snowflake_prod.DWH |
SALES.ORDERS |
snowflake_prod.DWH.SALES.ORDERS |
| CSV / SQLite (sem schema) | files.default.default |
vendas |
files.default.default.vendas |
Nomes com . são citados como no OpenMetadata ("v1.2").
Exceções (opcional), uma por linha, dataset = FQN:
crm.clientes = dwh_prod.core.crm.customers
staging.tmp_import =
Sem FQN depois do =, o dataset não é publicado.
4. Validar
«Publicar agora» reenvia o último scan concluído da conexão e mostra o estado:
- publicado — todos os datasets mapeados foram publicados;
- publicado em parte — alguns datasets sem FQN, sem tabela no catálogo (FQN errado?) ou com erro; a mensagem lista os primeiros;
- falhou — nada publicado (token revogado, servidor em baixo, nenhum FQN encontrado).
Daí em diante, cada scan concluído publica automaticamente (desliga com «Publicar automaticamente no fim de cada scan»).
Comportamento em falha
- Best-effort: o push corre no worker depois de o scan ficar
completed. Nenhum erro do catálogo falha, atrasa o estado ou repete o scan. - Retry leve: erros de rede,
429e5xxsão repetidos 2 vezes (0,5 s e 1,5 s).4xxsão definitivos. Após 3 datasets seguidos com erro e nenhum sucesso, o push para (catálogo em baixo / token revogado) em vez de martelar o servidor. - Erros ficam no estado da conexão e no log
catalog_push_*— sempre sem o token (as mensagens do servidor passam porscrub). - Compatível com OpenMetadata ≥ 1.5 (
POST /dataQuality/testCases/testCaseResults/{fqn}) e com versões 1.x anteriores (PUT /dataQuality/testCases/{fqn}/testCaseResult, usado automaticamente quando o endpoint novo devolve 404).
API
| Método | Caminho | Quem |
|---|---|---|
GET |
/catalog-integrations |
membros |
POST |
/catalog-integrations {name, kind: "openmetadata" | "datahub" | "collibra" | "purview", api_url, token} (+ username, community, domain no Collibra; + tenant_id, client_id no Purview, com o client secret em token) |
admin |
POST |
/catalog-integrations/{id}/test |
admin |
DELETE |
/catalog-integrations/{id} (apaga também os mapeamentos) |
admin |
GET |
/catalog-syncs (estado de todas as conexões) |
membros |
GET |
/connections/{id}/catalog-sync |
membros |
PUT |
/connections/{id}/catalog-sync {integration_id, fqn_prefix, overrides, enabled} |
admin |
DELETE |
/connections/{id}/catalog-sync |
admin |
POST |
/connections/{id}/catalog-sync/push |
admin |
Todas as alterações ficam no trilho de auditoria (catalog_integration_*,
catalog_sync_*).
DataHub (CATSYNC v2)
Mesmo desenho, pelo endpoint oficial do REST emitter do DataHub (GMS). O
BeryX escreve só entidades assertion próprias
(urn:li:assertion:datavault-<hash>, determinístico por dataset + teste —
cada scan atualiza a mesma assertion) com dois aspetos:
| Aspeto | Conteúdo |
|---|---|
assertionInfo |
type: DATASET, datasetAssertion com o URN do dataset, scope DATASET_COLUMN (+ fields = schemaField da coluna) ou DATASET_ROWS, operator: _NATIVE_, nativeType datavault_<tipo> / datavault_quality_verdict; source: EXTERNAL; descrição |
assertionRunEvent |
resultado deste scan: SUCCESS / FAILURE / ERROR (sem regras), nativeResults com a mensagem + verdict, score, failed_rules ou severity, rule_type |
Os testes publicados são os mesmos do OpenMetadata (veredito + uma por regra, mesmas regras de estado). As assertions aparecem na aba Quality / Validation do dataset.
Não escrevemos o aspeto
testResultsdo dataset: pertence às Metadata Tests do próprio DataHub e seria sobrescrito. Nenhum aspeto do dataset (schema, owners, tags…) é tocado.
Pedidos (todos com Authorization: Bearer <PAT>):
POST /api/graphql { me } — «Testar ligação»
GET /aspects/{urn}?aspect=datasetKey&version=0 — dataset existe?
POST /aspects?action=ingestProposal assertionInfo + assertionRunEvent
Setup
- Token: com a autenticação do metadata service ativa, cria um utilizador de serviço com permissão de editar assertions (política Edit Assertions nos datasets) e gera um Personal Access Token em Settings → Access Tokens.
- Ligar: Conexões → Catálogo externo → Ligar outro catálogo (ou
«Ligar um catálogo»), escolhe DataHub e indica o URL do GMS — ex.:
https://datahub.empresa.pt/api/gms(proxy do frontend),https://datahub-gms.empresa.pt:8080ouhttps://<tenant>.acryl.io/gms(aceita também o URL terminado em/api/graphql). Mesma proteção anti-SSRF e cifra Fernet do token. - Mapear: no DataHub a tabela é o URN
urn:li:dataset:(urn:li:dataPlatform:<plataforma>,<nome>,<ENV>). O prefixo éplataforma[@ENV].base— o BeryX acrescentaschema.tabelaao nome (ENV por omissãoPROD):
| Prefixo | Dataset | URN publicado |
|---|---|---|
postgres.analytics |
crm.clientes |
urn:li:dataset:(urn:li:dataPlatform:postgres,analytics.crm.clientes,PROD) |
snowflake@DEV.dwh |
sales.orders |
urn:li:dataset:(urn:li:dataPlatform:snowflake,dwh.sales.orders,DEV) |
postgres.prod-instance.analytics (platform instance no nome) |
crm.clientes |
…postgres,prod-instance.analytics.crm.clientes,PROD) |
Exceções: dataset = urn:li:dataset:(…) (usado tal e qual) ou
dataset = plataforma[@ENV].nome; vazio = não publicar. Prefixo/URN com
sintaxe inválida é recusado ao guardar (400).
Compatibilidade: DataHub ≥ 0.12 (campos source/description do
assertionInfo) e DataHub Cloud.
Collibra (CATSYNC v3)
Estado: validado com transporte simulado (a API do Collibra substituída por um servidor falso nos testes, com o CSV e o template verificados por snapshot). A validação num tenant Collibra de um cliente está pendente — até lá, confirma os três pressupostos de Por confirmar no tenant.
Pela Import REST API v2 do Collibra, como no exemplo oficial
«data-quality»
(«Import Data Quality Metrics from an external system»): um CSV + um template
JSON num único POST /rest/2.0/import/csv-job, seguido de polling do job.
Ao contrário do OpenMetadata e do DataHub, que recebem um pedido por teste, o
Collibra recebe um job de import por push com todos os testes do scan.
O que aparece no Collibra
Um asset do tipo OOTB Data Quality Metric por teste (veredito + uma regra por linha, as mesmas do OpenMetadata), no domínio Rulebook configurado:
| Campo do asset | Veredito (datavault_quality) |
Regra (datavault_<tipo>_<id>) |
|---|---|---|
| Nome completo | DataVault:<id do dataset>:datavault_quality |
DataVault:<id do dataset>:datavault_<tipo>_<id8> |
| Display name | clientes · veredito DataVault |
clientes · not_null (email) |
| Passing Fraction | score 0-100 | % de linhas que passam (ou 100/0, ver abaixo) |
| Threshold | — | % mínima para passar = 100 − limiar do BeryX |
| Loaded / Rows Passed / Rows Failed | — | contagens da regra, quando existem |
| Result | true se pass/warn, false se fail |
true se passou |
| Description | resumo + link para o dataset no BeryX + data do scan | idem |
| Relação | «complies to / applies to» (…7018) com a Table |
com a Column (ou a Table, se a regra é de tabela) |
- O nome completo é estável: cada scan atualiza o mesmo asset (upsert por nome + domínio da Import API) — sem estado extra no BeryX.
- Contagens: só agregados que o motor de regras já guarda —
not_null(linhas e nulos),unique(linhas e repetidas) e as regras com valores verificados/violações (pattern,membership,schema_conformity…). Regras sem contagem (frescura, volume, erro de avaliação) publicam uma métrica binária: Passing Fraction 100 se passou, 0 se falhou, sem linhas nem limiar. Nunca vão valores nem exemplos de linhas. - Uma célula vazia no CSV deixa o atributo por enviar (ex.: Threshold do veredito).
Pedidos
Todos com Authorization: Basic <utilizador:password> ou Bearer <JWT>:
GET /rest/2.0/users/current — «Testar ligação»
GET /rest/2.0/assets?name=<nome completo>&nameMatchMode=EXACT — a Table existe?
GET /rest/2.0/relations?targetId=<table>&relationTypeId=…7042 — colunas da tabela
POST /rest/2.0/import/csv-job multipart: file (CSV), template, separator «;», headerRow, continueOnError
GET /rest/2.0/jobs/{jobId} — polling a cada 2 s, teto de 30 s
O template declara a community e o domínio (tipo Rulebook) de destino — o import cria-os se não existirem, como no exemplo oficial — e um asset por linha do CSV identificado por nome + domínio, ligado ao asset alvo pelo id.
Setup
- Utilizador: um utilizador de serviço com permissão para importar e criar assets do tipo Data Quality Metric no domínio de destino (e para criar a community/domínio, se ainda não existirem), e para ver as Tables e Columns das fontes. Autenticação Basic (utilizador + password) ou um JWT do Collibra (deixar o utilizador vazio).
- Ligar: Conexões → Catálogo externo → Ligar um catálogo → Collibra:
- URL do Collibra:
https://empresa.collibra.com(aceita também…/rest/2.0). Mesma proteção anti-SSRF e mesma cifra Fernet (password, utilizador e JWT ficam cifrados; nunca voltam a ser mostrados nem aparecem em logs ou mensagens de erro). - Community de destino e Domínio de destino (Rulebook): onde ficam
as métricas — ex.:
Data Quality/DataVault. Recomendado um domínio Rulebook só para o BeryX. - «Ligar e testar» confirma URL e credenciais (
GET /users/current).
- URL do Collibra:
- Mapear: no Collibra a tabela é a Table com o nome completo da
convenção do Data Catalog, com
>como separador. O prefixo é tudo antes de>schema>tabela:
| Prefixo | Dataset | Table procurada |
|---|---|---|
postgres_prod>analytics |
crm.clientes |
postgres_prod>analytics>crm>clientes |
SQLite>erp |
clientes (sem schema) |
SQLite>erp>clientes |
Exceções: dataset = <nome completo da Table> (usado tal e qual — serve para
Tables registadas à mão com outros nomes); vazio = não publicar. As colunas são
as Columns ligadas à Table pela relação OOTB «Column is part of Table»
(…7042), comparadas pelo último segmento do nome (sem distinguir maiúsculas).
Comportamento em falha
- Table inexistente (ou nome ambíguo — várias Tables com o mesmo nome completo): o dataset fica em «sem tabela no catálogo» e a mensagem diz porquê; nunca exceção.
- Column inexistente: a métrica liga-se à Table e a mensagem do push lista as colunas em falta («N métrica(s) ligada(s) à tabela por falta do asset Column»).
- Job:
COMPLETED/SUCCESS→ publicado;COMPLETED_WITH_ERROR(continueOnError: os comandos válidos entram) → publicado em parte, com a mensagem do Collibra;ERROR/CANCELED/FAILURE→ falhou (nada do lote conta como publicado). Um job ainda a correr ao fim de 30 s fica «submetido» (não é erro): o resultado fica nas Activities do Collibra. - Como nos outros catálogos, nenhum erro do Collibra falha, atrasa o estado ou
repete o scan. O push corre no fim do scan (sem fila própria); o erro fica no
estado da conexão e no log
catalog_push_*com ocorrelation_iddo scan e o id do job — sempre sem credenciais. - Ler do catálogo (cartão #140) ainda não é suportado no Collibra: a
opção «Importar do catálogo ao sincronizar» fica escondida e
POST /connections/{id}/catalog-import/planresponde 400.
Por confirmar no tenant
O cliente HTTP segue o exemplo oficial e a especificação OpenAPI publicada no
mesmo repositório (import-api-java-client/schemas/). Três pontos só um tenant
real confirma:
- O atributo OOTB Description está atribuído ao tipo Data Quality Metric
(os outros seis atributos são os do exemplo oficial). Se não estiver, o job
termina
COMPLETED_WITH_ERROR(publicado em parte) até a atribuição existir. - Os nomes completos das Tables seguem a convenção
sistema>base>schema>tabelada ingestão do Data Catalog — senão, usar exceções com o nome completo real. - Uma célula vazia no CSV não apaga um valor já existente no atributo.
Microsoft Purview (cartão #398)
Estado: validado com transporte simulado. O Data Map e o login do Entra ID são substituídos por um
httpx.MockTransportque aplica a semântica de substituição docreateOrUpdatee recusa pedidos sem os atributos obrigatórios; o corpo do pedido tem snapshot embackend/tests/snapshots/purview/. Sem tenant Purview nesta sessão, o spike confirmou oscustomAttributesna especificação oficial da API do Data Map (2023-09-01, repositórioAzure/azure-rest-api-specs): o modeloAtlasEntitytemcustomAttributese o exemploEntity_AddCustomAttributeusa-os emPOST /atlas/v2/entity. A validação num tenant de um cliente está pendente — ver Por confirmar no tenant.
Pela API do Data Map (compatível com Apache Atlas v2), o BeryX escreve
quatro atributos personalizados (customAttributes) na entidade da tabela
(ATTRIBUTE_KEYS em backend/app/infra/catalog/purview.py):
| Atributo | Valor |
|---|---|
datavault_score |
score 0-100 do último scan, com duas casas (72.50); n/a sem score |
datavault_verdict |
pass, warn, fail ou unknown (o mesmo veredito do gate de CI) |
datavault_last_scan |
fim do scan em UTC, ISO 8601 (2026-10-11T11:00:00Z) |
datavault_url |
página do dataset no BeryX (app_base_url/datasets/{id}) |
Só o veredito sobe. As regras, uma a uma, não vão para o Purview nesta fase: o
push manda-lhe só o caso do veredito (CatalogSpec.publishes_rules=False), e o
estado da conexão conta um teste por dataset.
Pedidos
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token client credentials, scope https://purview.azure.net/.default
GET /datamap/api/atlas/v2/types/entitydef/name/DataSet testar a ligação
GET /datamap/api/atlas/v2/entity/uniqueAttribute/type/{typeName}?attr:qualifiedName=…&ignoreRelationships=true&minExtInfo=true
POST /datamap/api/atlas/v2/entity?api-version=2023-09-01 createOrUpdate
O createOrUpdate é uma atualização completa da entidade. Para não apagar nada
do cliente, o pedido reenvia o typeName, o guid, os attributes, os
contacts e os labels tal como o GET os devolveu e junta os quatro atributos
do BeryX aos customAttributes que já lá estavam (update_payload).
Classificações, relações e business attributes não vão no pedido. Quando os
quatro valores já são os do scan, não há escrita. O pedido não leva controlo de
concorrência: uma edição feita no Purview entre o GET e o POST do mesmo push é
reescrita com os valores lidos no GET.
O login no Entra ID tem um cliente HTTP próprio, com o mesmo guard anti-SSRF e
IP fixado que o endpoint do Purview (CatalogSpec.auth_url). O token de acesso
é pedido uma vez por push e fica só em memória; o client secret e o token
nunca aparecem em mensagens nem em logs.
Setup
Service principal: registar uma app no Entra ID, criar-lhe um client secret e, no Data Map do Purview, dar-lhe a role Data Curator na coleção onde estão as tabelas (a role que lê e altera assets).
Ligar: Conexões → Catálogo externo → Ligar um catálogo → Microsoft Purview:
- Endpoint da conta Purview:
https://<conta>.purview.azure.com(aceita também…/datamap/api); - Tenant (Directory ID) do Entra ID: o GUID do diretório ou um domínio
verificado (
empresa.onmicrosoft.com); - Client ID da app registada: o GUID da app;
- Client secret: cifrado com Fernet, nunca volta a ser mostrado.
O tenant e o client ID não são segredos e ficam em claro em
catalog_integrations.settings_json(sem migration: a coluna existe desde o Collibra). Ligar e testar pede um token ao Entra ID e lê a definição do tipoDataSetno Data Map: confirma as credenciais e a leitura. A permissão de escrita só se confirma no primeiro push.- Endpoint da conta Purview:
Mapear: o prefixo é o
qualifiedNameda base no Purview; o BeryX acrescenta/schema/tabelae procura a entidade pelo tipo e peloqualifiedName. O tipo (typeName) deduz-se do host onde a especificação o mostra; nas outras fontes escreve-se «tipo:qualifiedName»:Fonte Prefixo typeNameAzure SQL Database mssql://<servidor>.database.windows.net/<base>azure_sql_table, deduzidoAzure SQL Managed Instance mssql://<host>.database.windows.net:3342/<base>azure_sql_mi_table, deduzido da porta 3342Synapse, pool dedicado do workspace mssql://<workspace>.sql.azuresynapse.net/<pool>azure_synapse_dedicated_sql_table, deduzidoPool dedicado autónomo (ex-SQL DW) azure_sql_dw_table:mssql://<servidor>.database.windows.net/<dw>explícito: o host é igual ao do Azure SQL Database Synapse serverless, SQL Server, Fabric e outros <typeName>:<qualifiedName>explícito No Fabric o
qualifiedNamede uma tabela de Lakehouse acaba em…/tables/dbo%252Fminha_tabela(o schema e a tabela vão juntos, com a barra codificada), e por isso não se forma com prefixo +/schema/tabela: usa-se uma exceção por dataset com oqualifiedNamecompleto copiado do Purview. As exceções aceitam a mesma forma[tipo:]qualifiedName. Em produção, o conector Fabric não vem na imagem gerida (ver as notas honestas em CONECTORES.md); as fontes Microsoft que chegam ao Purview por este caminho são o Azure SQL e o Synapse, pelo conectormssql.
Comportamento em falha
- Entidade não encontrada (404 do GET, ou resposta sem entidade): o dataset fica em «sem tabela no catálogo» com a referência tentada («tipo:qualifiedName»). Um tipo errado aparece aqui.
- 401 do Entra ID (secret errado ou expirado) ou do Data Map (token
recusado): o push fica falhou com «Entra ID devolveu 401: …» ou
«Microsoft Purview devolveu 401: …», sem segredo nem token. O log
catalog_push_dataset_failedleva ocorrelation_iddo scan; três falhas seguidas sem nenhum sucesso param o push. - 429 e 5xx: duas tentativas extra com espera curta, como nos outros catálogos.
- Como nos outros catálogos, nenhum erro do Purview falha o scan.
- Ler do catálogo (cartão #140) ainda não é suportado no Purview: a
opção fica escondida e
POST /connections/{id}/catalog-import/planresponde
Fora desta fase
- Classificação «BeryX verdict»: exige criar a definição de tipo da classificação e aplicá-la à entidade; fica para um cartão seguinte.
- Data Quality API do Unified Catalog: segundo a página da Microsoft (learn.microsoft.com/rest/api/purview/unified-catalog-data-quality), está em pré-visualização pública desde dezembro de 2025 e só cobre as funcionalidades GA. Publicar lá as regras fica para quando sair da pré-visualização.
- Clouds soberanas (Azure Government, Azure China): o login usa sempre
login.microsoftonline.come o scopehttps://purview.azure.net/.default.
Por confirmar no tenant (Purview)
- Entidade inexistente: o Apache Atlas responde 404 com
ATLAS-404-00-009; a especificação do Purview só documenta o corpo de erro genérico. O cliente trata o 404 e um 200 sem entidade como «não encontrada». - Reenvio dos atributos lidos: o
createOrUpdateaceita osattributes,contactselabelstal como o GET os devolve, sem recusar atributos só de leitura e sem duplicar contactos ou labels. - Tipos deduzidos:
azure_sql_table,azure_sql_mi_tableeazure_sql_dw_tablevêm dos exemplos da especificação (Discovery_Query_*);azure_synapse_dedicated_sql_tablevem do acelerador oficial de linhagem da Microsoft (Purview-ADB-Lineage-Solution-Accelerator), não do scanner. - Limites dos
customAttributes: no Apache Atlas, chave até 50 caracteres ([a-zA-Z0-9_-]) e valor até 500, por omissão; a documentação do Purview não publica limites. O BeryX corta cada valor a 500 caracteres.
Ler do catálogo (cartão #140)
Clientes com OpenMetadata ou DataHub (o Collibra e o Purview ainda não — ver acima) já curaram owners, domínios, tags (PII,
Sensitive…) e glossário. Em vez de os recriar à mão, o BeryX lê essa
curadoria pela mesma integração e pelo mesmo mapeamento dataset → FQN/URN da
conexão (prefixo + exceções). O catálogo nunca é escrito neste caminho:
| Catálogo | Pedidos (só leitura) |
|---|---|
| OpenMetadata | GET /api/v1/tables/name/{fqn}?fields=owners,domain,tags,columns · GET /api/v1/tables?database=… (listar) |
| DataHub | POST /api/graphql só com query — dataset(urn){ownership, domain, tags, glossaryTerms, schemaMetadata…} e search (listar) |
O cliente de leitura (ReadOnlyJsonHttp) recusa, antes de sair para a
rede, qualquer método que não seja GET/HEAD e, no DataHub, qualquer POST
que não seja uma query GraphQL (uma mutation é recusada). No OpenMetadata
os termos de glossário vêm nas tags com source=Glossary.
O que é importado
| Do catálogo | No BeryX | Regras |
|---|---|---|
| Domínio da tabela | Domínio (criado se não existir) + associação do dataset | Upsert pelo nome, sem distinguir maiúsculas nem espaços a mais. Nunca remove associações. |
| Owners (utilizadores) | Owner do domínio | Só se o domínio ainda não tiver owner e o email/username do catálogo corresponder a uma conta ativa do workspace. Sem correspondência → aviso (sem criar contas nem convites). Equipas/grupos são ignorados. |
| Tags das colunas | Nível de sensibilidade com origem catálogo |
Ver tabela abaixo. Nunca sobrepõe uma classificação manual. |
| Descrição da tabela | Dataset.description (docs_source = catalog) |
Só quando a descrição do BeryX está vazia. |
| Termos de glossário | — | Só contados; não entram na base de conhecimento nesta fase. |
Tags → nível de sensibilidade (último segmento da tag, sem maiúsculas; o nível mais alto ganha quando há várias):
| Tag no catálogo | Nível |
|---|---|
PII, qualquer PII.* (PII.Sensitive, PII.NonSensitive) |
Dados pessoais |
Sensitive, Confidential |
Confidencial |
Internal |
Interno |
Public |
Público |
Precedência: manual > catálogo > automático. O import cria a
classificação se a coluna não tiver nenhuma, atualiza uma que já veio do
catálogo, e só sobe uma automática (nunca desclassifica PII detetado). A
deteção de PII dos scans continua a poder subir uma classificação do catálogo
para «Dados pessoais». Cada mudança fica no rasto da coluna (origem
catalog, nota com a tag). Tags só ao nível da tabela não classificam
colunas — ficam como aviso no plano.
Plano e aplicação
- Conexões → Catálogo externo → Ler do catálogo → Pré-visualizar import:
o plano lista o que muda (
+criar,~alterar), os avisos (owners sem conta, curadoria manual mantida, tabelas com erro) e as contagens. - Aplicar import volta a ler o catálogo e aplica numa transação. Um segundo apply dá 0 alterações (idempotente).
- Opção «Importar do catálogo ao sincronizar»: o import corre no fim de cada scan concluído, depois do push dos verdicts, numa transação própria — best-effort, nunca falha o scan.
Em falha: cada tabela é lida à parte — uma tabela com erro fica em aviso e o lote continua; tabela inexistente no catálogo (404) conta como «sem metadados». Três falhas seguidas sem nenhuma leitura bem-sucedida param a leitura (catálogo em baixo ou token revogado).
Privacidade: emails/usernames dos owners do catálogo são dados pessoais — só servem para procurar a conta do workspace; não vão para logs, nem para a auditoria, nem para a resposta do plano (que mostra o nome da conta do BeryX). O token nunca aparece em mensagens.
API
| Método | Caminho | Quem | Efeito |
|---|---|---|---|
POST |
/connections/{id}/catalog-import/plan |
admin | Dry-run: {changes, warnings, errors, summary, stats, text} |
POST |
/connections/{id}/catalog-import/apply |
admin | Aplica; auditoria catalog_import_applied (só contagens) |
PUT |
/connections/{id}/catalog-sync |
admin | Campo import_on_sync (omitido = mantém) |
O estado da conexão (GET /connections/{id}/catalog-sync) inclui
import_on_sync, last_import_at e last_import_stats
(tables, read, not_found, failed, skipped, changes…).
Permissões no catálogo: o bot/token só precisa de leitura de tabelas (OpenMetadata) ou de ver datasets (DataHub) para o import — as permissões de escrita de testes/assertions só são precisas para publicar.