Esquemas Remotos (Remote Schemas)¶
Uma fonte de esquema remoto conecta uma API externa — GraphQL, gRPC, ou REST (OpenAPI) — à camada semântica do Provisa. Uma vez registradas, as operações da API externa se tornam tabelas e funções Provisa de primeira classe. (REQ-308, REQ-316, REQ-325) Toda regra de governança, interface de consulta, e camada de segurança se aplica automaticamente. (REQ-310, REQ-319, REQ-328) O serviço remoto nunca vê as regras de governança do Provisa. (REQ-310, REQ-319, REQ-328)
Três tipos de fonte¶
Esquema remoto GraphQL (REQ-307–313)¶
Como registrar. Faça POST para /admin/sources/graphql-remote com a URL do endpoint, um
namespace, e auth opcional. O Provisa dispara uma consulta de introspecção __schema padrão contra
o endpoint remoto. (REQ-307) [tool-verified: provisa/graphql_remote/introspect.py:47–59]
{
"source_id": "petstore-gql",
"url": "https://api.example.com/graphql",
"namespace": "petstore",
"domain_id": "veterinary",
"auth": { "type": "bearer", "token": "..." },
"cache_ttl": 300,
"field_overrides": { "createPet": "query" },
"relationships": [
{ "source_table": "petstore__pets", "source_column": "owner_id",
"target_table": "owners__users", "target_column": "id" }
]
}
Opções de auth: none, bearer (header Authorization), basic (Base64 username:password).
(REQ-307) [tool-verified: provisa/graphql_remote/introspect.py:36–45]
Overrides de campo. field_overrides é um mapa {fieldName: "query" | "mutation"} aplicado
após a introspecção. Tem prioridade sobre a classificação estrutural. Somente campos do tipo query
podem ser reclassificados como mutations; campos do tipo mutation não têm caminho de override no
GraphQL. (REQ-531) [tool-verified: provisa/graphql_remote/mapper.py]
Relacionamentos no momento do registro. relationships declara caminhos de join FK/PK entre
tabelas no momento do registro. Estes são armazenados como relacionamentos declarados manualmente
(sem flag remote_managed). Na atualização, relacionamentos auto-detectados (aqueles com
remote_managed: True) são reexecutados e podem mudar; relacionamentos declarados manualmente não
são tocados. (REQ-554) [tool-verified: provisa/api/admin/graphql_remote_router.py]
O que é auto-descoberto. Todo campo no tipo Query remoto que retorna um OBJECT se torna uma
tabela virtual. Todo campo no tipo Mutation remoto se torna uma função rastreada. (REQ-308)
[tool-verified: provisa/graphql_remote/mapper.py:243–278]
Nomeação de tabela. Tabelas são nomeadas {namespace}__{field_name}. Com namespace petstore
e um campo de consulta pets: o nome da tabela é petstore__pets. (REQ-312) [tool-verified:
provisa/graphql_remote/mapper.py:250]
Mapeamento de tipo (REQ-308). Campos escalares mapeiam diretamente para tipos Provisa. Campos
OBJECT se dividem em dois casos dependendo se o tipo alvo é governado (veja "Tabelas governadas"
abaixo). [tool-verified: provisa/graphql_remote/mapper.py:14–36,
provisa/api/data/endpoint.py:655–671, provisa/compiler/schema_gen.py:481–485]
| Tipo GraphQL | Tipo Provisa |
|---|---|
String |
text |
ID |
text |
Int |
integer |
Float |
numeric |
Boolean |
boolean |
OBJECT (tipo inline não governado, ex.: ContactInfo) |
coluna blob jsonb |
| OBJECT (tipo alvo governado) | excluído inteiramente da SDL e da busca |
| Qualquer ENUM | jsonb |
| Escalar customizado | text (padrão) |
Tabelas governadas. Um tipo GQL é governado quando aparece como um campo raiz Query no
esquema remoto. _collect_queryable_types coleta estes durante o registro, preferindo campos sem
argumento obrigatório para que possam ser buscados em massa como alvos de join. [tool-verified:
provisa/graphql_remote/mapper.py:395–413]
Quando uma coluna do tipo OBJECT em uma tabela governada aponta para outro tipo governado, essa
coluna está sujeita a três regras simultaneamente [tool-verified:
provisa/api/data/endpoint.py:655–671, provisa/compiler/schema_gen.py:481–485]:
- Excluída da busca GQL — o campo não é solicitado ao buscar linhas da tabela pai.
- Excluída da SDL — o campo não aparece no tipo pai no esquema gerado.
- Acessível somente via um relacionamento declarado — um steward deve registrar um JOIN entre as duas tabelas governadas materializadas. Sem um, o campo simplesmente está ausente; não há fallback de blob.
Tipos OBJECT que NÃO são alcançáveis como campos Query raiz (tipos inline como ContactInfo ou
Address) seguem regras diferentes: são buscados como colunas blob jsonb e aparecem na SDL como
campos de objeto aninhado. Sub-campos são acessíveis via extração -->> em SQL.
Argumentos obrigatórios. Quando um campo de consulta raiz tem argumentos non-null sem valor
padrão, esses se tornam colunas native_filter_type: query_param na tabela (prefixadas com _nf_
no momento da injeção). O executor as passa como variáveis GraphQL. (REQ-555) [tool-verified:
provisa/graphql_remote/mapper.py:110–120, provisa/api/app.py:1280–1303]
Relacionamentos detectados automaticamente. O Provisa varre as colunas do tipo OBJECT de cada
tabela. Quando o tipo GQL referenciado também é registrado como uma tabela na mesma fonte, um
relacionamento é emitido. Relacionamentos many-to-one inferem colunas de origem e alvo a partir de
convenções de nomenclatura (breedName no tipo de origem → name no tipo alvo Breed). Campos
one-to-many (LIST) emitem relacionamentos com referências de coluna vazias — a FK vive no lado
alvo. (REQ-554) [tool-verified: provisa/graphql_remote/mapper.py:162–202]
Mutations. Campos de mutation produzem funções rastreadas com tipos de argumento mapeados dos
args da mutation e um return_schema derivado do tipo de retorno da mutation. (REQ-308)
[tool-verified: provisa/graphql_remote/mapper.py:261–278]
Atualização (refresh). Faça POST para /admin/sources/graphql-remote/{id}/refresh.
Reintrospecciona o esquema remoto e atualiza os registros de tabela e função. Regras de governança
existentes (RLS, mascaramento) são preservadas. (REQ-311) [tool-verified:
provisa/api/admin/graphql_remote_router.py:217–257]
Limitações.
- Campos de consulta raiz escalares e ENUM (tipo de retorno não é OBJECT) se tornam funções
rastreadas, não tabelas virtuais. Seu
return_schemaé uma única colunavaluedo tipo escalar mapeado. [tool-verified:provisa/graphql_remote/mapper.py:254–279] - O aninhamento de objeto é resolvido no momento do registro até
graphql_remote.max_object_depth(padrão: 5). Tanto a seleção de busca remota quanto os metadados de sub-campo são construídos até essa profundidade; campos além do limite não são buscados e não estão disponíveis para extração SQL. (REQ-556) [tool-verified:provisa/graphql_remote/mapper.py:38–52] - Campos OBJECT aninhados do tipo LIST (ex.:
breed.awards: [Award]) são incluídos na seleção de busca até os níveis de aninhamentographql_remote.max_list_depth(padrão: 2). Dentro desse limite, a lista é buscada como um arrayjsonbna coluna pai, e a seleção GQL injetafirst: Nonde N égraphql_remote.max_list_items(padrão: 100) para limitar o tamanho do array. Além demax_list_depth, o campo LIST é excluído inteiramente para prevenir expansão ilimitada de dados. Em SQL, o array é acessado viajson_array_elements(column_name)ou extração de índice->>. Se o tipo de item da lista tem sua própria consulta raiz, registre-o como uma tabela separada e crie um relacionamento em vez disso — o caminho de join é mais eficiente e evita o blob. (REQ-556) [tool-verified:provisa/graphql_remote/mapper.py:43–70] - Para consultas SQL, colunas do tipo OBJECT não governadas são buscadas por completo do remoto
(todos os sub-campos até a profundidade configurada) e cacheadas como
jsonb. O acesso a sub-campo em SQL é tratado via extração->>contra o blob; a requisição remota não é reduzida somente aos campos que a consulta SQL seleciona. Quando o tipo de item da LIST não tem consulta raiz e a representação em blob é insuficiente, escreva a consulta em SDL GraphQL diretamente — o Provisa reproduz fielmente a seleção de campo GQL, para que o remoto veja exatamente os campos solicitados. [tool-verified:provisa/compiler/sql_gen.py:1332–1368] - Se o servidor remoto rejeitar um campo do tipo OBJECT porque requer seleção de sub-campo (o que
não deveria ocorrer quando
gql_selectionestá disponível), o executor tenta novamente uma vez com esses campos removidos para que colunas escalares ainda sejam retornadas. [tool-verified:provisa/graphql_remote/executor.py:76–80]
Esquema remoto gRPC (REQ-322–329)¶
Como registrar. Faça POST para /admin/grpc-remote/register com o endereço do servidor, um
caminho ou URL para um arquivo .proto, e config TLS opcional.
{
"source_id": "orders-grpc",
"proto_path": "https://api.example.com/orders.proto",
"server_address": "grpc.example.com:443",
"namespace": "orders",
"domain_id": "commerce",
"tls": true,
"cache_ttl": 300,
"method_overrides": { "CreateOrder": "query" },
"relationships": [
{ "source_table": "orders__OrderService__ListOrders", "source_column": "customer_id",
"target_table": "customers__CustomerService__GetCustomer", "target_column": "id" }
]
}
O Provisa busca o proto, o analisa com um parser de texto puro (sem dependências proto externas no
momento da análise), compila stubs Python via grpc_tools.protoc, e abre um grpc.aio.Channel
persistente. (REQ-322) [tool-verified: provisa/grpc_remote/loader.py:99–128,
provisa/grpc_remote/loader.py:166–214, provisa/api/admin/grpc_remote_router.py:80–104]
Arquivos proto também podem ser caminhos locais. Caminhos de importação para tipos bem conhecidos
(google/protobuf/timestamp.proto) são armazenados no momento do registro e reutilizados na
atualização. (REQ-329) [tool-verified: provisa/grpc_remote/loader.py:135–159]
O que é auto-descoberto. Todo método rpc no proto é classificado como query ou mutation
usando três sinais em ordem de prioridade: (REQ-323) [tool-verified: provisa/grpc_remote/mapper.py]
method_overridesno payload de registro —{"MethodName": "query"}ou{"MethodName": "mutation"}sobrepõe todo o resto.server_streaming: true— o servidor envia um stream de mensagens; sempre uma tabela virtual (a menos que a saída seja um escalar).- A mensagem de saída tem um campo repetido do tipo mensagem — ex.:
ListOrdersResponse { repeated Order items; }é tratado como um envoltório de lista e se torna uma tabela virtual. Campos escalares repetidos (ex.:repeated string tags) não disparam isso — são propriedades de array em uma única entidade, não fontes de linha.
Métodos que não correspondem a nenhum desses sinais (RPC unário retornando uma única mensagem de entidade, ou qualquer saída escalar) se tornam funções rastreadas.
Nomeação de tabela. O nome padrão é {namespace}__{ServiceName}__{MethodName}. Sem um
namespace, os nomes de serviço e método são unidos diretamente. Qualquer tabela registrada pode
receber um alias; quando definido, o alias é o nome usado em todo lugar (consultas, SDL,
relacionamentos). O nome auto-gerado é a chave de registro e nunca muda. (REQ-322) [tool-verified:
provisa/core/repositories/table.py:129–134]
Mapeamento de tipo (REQ-324). Tipos escalares proto mapeiam para tipos SQL como segue.
[tool-verified: provisa/grpc_remote/mapper.py:31–47]
| Tipo Proto | Tipo SQL |
|---|---|
string, bytes |
text |
int32 / uint32 / sint32 / fixed32 / sfixed32 |
integer |
int64 / uint64 / sint64 / fixed64 / sfixed64 |
bigint |
float |
real |
double |
numeric |
bool |
boolean |
repeated <T> |
jsonb |
| Mensagem aninhada | jsonb |
| Enum | text |
Relacionamentos no momento do registro. relationships funciona de forma idêntica ao adapter
GQL — declara caminhos de join FK/PK armazenados como relacionamentos declarados manualmente (sem
flag remote_managed). Na atualização, estes são preservados sem alteração. (REQ-554)
[tool-verified: provisa/api/admin/grpc_remote_router.py:93–109]
Métodos de query (REQ-325). Campos da mensagem de saída se tornam colunas de tabela. Campos da
mensagem de entrada se tornam tanto argumentos GraphQL passados à chamada remota quanto são
registrados como colunas prefixadas com _nf_ com native_filter_type: "grpc_input" — o mesmo
mecanismo que GQL e OpenAPI usam para injeção de filtro nativo. (REQ-555) [tool-verified:
provisa/api/admin/grpc_remote_router.py:207–213]
Sub-campos de mensagem aninhada. Para métodos de query, campos do tipo mensagem não repetidos
na profundidade 0 (colunas de saída diretas) têm seus sub-campos resolvidos um nível mais profundo
e armazenados como object_fields no ColumnDef. Este metadado é usado para extração de sub-campo
jsonb em SQL e para documentação de esquema. Campos aninhados além da profundidade 1 não são
expandidos recursivamente. (REQ-556) [tool-verified: provisa/grpc_remote/mapper.py:111–128]
Métodos server-streaming coletam todas as mensagens transmitidas em uma lista antes de retornar
linhas. (REQ-325) [tool-verified: provisa/grpc_remote/executor.py:86–119]
Métodos de mutation (REQ-326). Campos da mensagem de entrada se tornam argumentos de entrada da
mutation. O esquema da mensagem de saída se torna o return_schema. [tool-verified:
provisa/grpc_remote/executor.py:122–143]
Gerenciamento de canal. Um grpc.aio.Channel por fonte registrada é armazenado no estado da
app e reutilizado através das requisições. O canal antigo é fechado antes que um novo abra na
atualização. (REQ-327) [tool-verified: provisa/api/admin/grpc_remote_router.py:107–117]
Atualização (refresh). Faça POST para /admin/grpc-remote/refresh/{source_id}. Recarrega o
proto do caminho armazenado, recompila stubs, e re-registra tabelas e funções. Alternativamente,
faça PUT para /admin/grpc-remote/{source_id}/proto com novo proto_text para atualizar o proto
inline. (REQ-329) [tool-verified: provisa/api/admin/grpc_remote_router.py:241–268,
provisa/api/admin/grpc_remote_router.py:300–358]
Limitações.
- A extração de objeto de sub-campo é de um nível de profundidade. Campos de mensagem aninhados
além da profundidade 1 não são expandidos recursivamente. (REQ-556) [tool-verified:
provisa/grpc_remote/mapper.py:111–128]
OpenAPI / REST (REQ-314–321)¶
Como registrar. Chame auto_register_openapi_source com um ID de fonte, uma spec analisada, e
metadados de conexão. A spec é carregada de um arquivo local ou URL. (REQ-314) [tool-verified:
provisa/openapi/loader.py:30–55, provisa/openapi/register.py:249–264]
Payload de registro. O endpoint /admin/openapi/register aceita dois campos adicionais ao
lado de source_id, spec_path, etc.:
{
"operation_overrides": { "createPet": "query", "listOrders": "mutation" },
"relationships": [
{ "source_table": "pets__listPets", "source_column": "owner_id",
"target_table": "owners__listOwners", "target_column": "id" }
]
}
O que é auto-descoberto. Toda operação GET na spec se torna uma tabela virtual, a menos que seu
esquema de resposta seja um tipo escalar (string, number, boolean, integer) — GETs que
retornam escalar se tornam funções rastreadas com uma única coluna value em vez disso. Toda
operação não-GET (POST, PUT, PATCH, DELETE) se torna uma função rastreada. (REQ-316, REQ-317)
Prioridade de classificação: operation_overrides (payload) sobrepõe x-provisa-kind (extensão de
spec) sobrepõe a heurística GET. operation_overrides é o caminho de override recomendado;
x-provisa-kind é para quando a própria spec deve carregar a classificação. (REQ-408)
[tool-verified: provisa/openapi/mapper.py:192–203]
Relacionamentos no momento do registro. relationships funciona de forma idêntica aos outros
adapters — armazenado como relacionamentos declarados manualmente, preservado na atualização.
(REQ-554) [tool-verified: provisa/api/admin/openapi_router.py:103–108]
Nomeação de tabela. Tabelas usam o operationId da operação. Se nenhum operationId for
definido, o Provisa faz slugify de {method}_{path}. Um alias é derivado removendo o segmento de
verbo inicial e singularizando o substantivo (findPetsByStatus → pet_by_status). (REQ-557)
[tool-verified: provisa/openapi/register.py:39–56]
Mapeamento de tipo. Tipos JSON Schema mapeiam para tipos Provisa como segue. [tool-verified:
provisa/openapi/register.py:59–70]
| Tipo JSON Schema | Tipo Provisa |
|---|---|
string |
string |
integer |
integer |
number |
number |
boolean |
boolean |
array |
jsonb |
object |
jsonb |
Parâmetros como colunas de filtro nativo. Parâmetros de path e query que ainda não são campos
de resposta se tornam colunas com native_filter_type definido como path_param ou query_param,
prefixadas com _nf_. Quando o nome de um parâmetro corresponde a um nome de campo de resposta, o
metadado do parâmetro é mesclado na entrada de coluna existente em vez de criar uma duplicata.
(REQ-555) [tool-verified: provisa/openapi/register.py:116–122,
provisa/openapi/register.py:172–196]
Resolução de esquema de resposta. O mapper verifica responses.200, depois responses.2xx,
depois responses.default. Respostas do tipo array são desempacotadas para o esquema do item.
Referências $ref são resolvidas um nível de profundidade. (REQ-316) [tool-verified:
provisa/openapi/mapper.py:83–101]
Sub-campos de objeto. Propriedades de resposta com type: object e suas próprias properties
são armazenadas como object_fields na coluna. Esses sub-campos são visíveis na SDL e usados para
extração jsonb em consultas. (REQ-556) [tool-verified: provisa/openapi/register.py:87–96]
Cache de resposta (REQ-318). Resultados de operação GET são cacheados no PostgreSQL por
pg_cache.py. Cada combinação de parâmetros de requisição obtém seu próprio grupo _params_hash.
Linhas para um dado hash são substituídas quando o TTL expira. Endpoints com parâmetro de path
(/pets/{id}) pulam a busca em massa inicial — a tabela de cache é criada vazia para introspecção
de esquema, depois populada por PK conforme as requisições chegam. [tool-verified:
provisa/openapi/pg_cache.py:181–234, provisa/openapi/pg_cache.py:307–360]
Atualização (REQ-321). Reanalise a spec e chame auto_register_openapi_source novamente.
Regras de governança existentes são preservadas; registros são atualizados com upsert ON CONFLICT.
[tool-verified: provisa/openapi/register.py:249–264]
Limitações.
- A extração de objeto de sub-campo é de um nível de profundidade. Propriedades aninhadas dentro de
object_fieldsnão são expandidas recursivamente. (REQ-556) [tool-verified:provisa/openapi/register.py:87–96] - Parâmetros de header e cookie são ignorados; somente parâmetros
pathequerysão registrados. (REQ-555) [tool-verified:provisa/openapi/mapper.py:144–158] - A resolução de
$refem nível de spec é de um nível de profundidade para esquemas de propriedade; referências de componente profundamente aninhadas podem não resolver. [tool-verified:provisa/openapi/mapper.py:51–60]
Impacto de registrar uma tabela remota¶
Uma tabela registrada a partir de qualquer fonte de esquema remoto é uma tabela Provisa de primeira classe. Nada nela é tratado de forma diferente de uma tabela relacional conectada localmente em tempo de execução. (REQ-308, REQ-313)
Interfaces de consulta. A tabela é imediatamente consultável via GraphQL, SQL (pgwire ou
direto), Cypher (GQL), JSON:API, e Arrow Flight. (REQ-001, REQ-267, REQ-345, REQ-257, REQ-051) A
geração de esquema sintetiza ColumnMetadata para tabelas remotas já que elas não têm catálogo — o
mapeamento de tipo é aplicado no momento da construção do esquema. (REQ-602) [tool-verified:
provisa/api/app.py:1367–1386]
Modelo de segurança. Todas as cinco camadas de governança se aplicam:
- Controle de acesso a domínio — o
domain_idda tabela condiciona quais funções conseguem vê-la. (REQ-039) [tool-verified:provisa/compiler/schema_gen.py:1064–1076] - Segurança em nível de linha (RLS) — filtros de linha configurados na tabela são injetados em toda consulta, independentemente da interface. (REQ-040, REQ-041)
- Visibilidade de coluna — a lista
visible_toem cada coluna controla a exposição de campo por função. (REQ-039) - Mascaramento de coluna — regras de mascaramento se aplicam no Estágio 2 do pipeline de governança. (REQ-040, REQ-263)
- Guard de predicado — colunas mascaradas são rejeitadas de cláusulas WHERE e HAVING. (REQ-603)
Consultas ad-hoc contra tabelas remotas são permitidas somente sob os direitos do usuário — o acesso é uniformemente baseado em direitos (direitos de tabela/coluna + relacionamentos aprovados), sem modo de governança por tabela. (REQ-001, REQ-003)
Governança de relacionamento (V002). Condições JOIN contra tabelas remotas — quando consultadas via SQL ou Cypher — devem corresponder a um relacionamento registrado e aprovado. (REQ-604) A verificação V002 é pulada para consultas GraphQL porque relacionamentos definidos na SDL são pré-aprovados por design. Veja docs/security.md.
Colunas do tipo OBJECT. Quando uma coluna mapeia para um OBJECT GQL inline não governado ou tipo
de objeto OpenAPI, seu tipo Provisa é jsonb. A coluna armazena o blob JSON aninhado completo.
Quando sub-campos são declarados (gql_object_fields ou object_fields), o mapa
gql_object_columns é populado no momento da construção do esquema. O gerador SQL usa esse mapa
para emitir expressões de extração ->> para sub-campos quando uma consulta os seleciona.
[tool-verified: provisa/api/app.py:1305–1315, provisa/compiler/schema_gen.py:80–82]
Args obrigatórios como parâmetros de filtro nativo. Campos de consulta raiz com args non-null,
sem padrão injetam colunas adicionais na tabela registrada. Essas colunas carregam
native_filter_type: query_param. O tradutor Cypher reescreve WHERE n.id = $val para
WHERE n._nf_id = $val, e o executor GraphQL as recolhe como variáveis para passar ao endpoint
remoto. (REQ-555) [tool-verified: provisa/api/app.py:1280–1303]
Impacto de criar um relacionamento de cobertura¶
Quando um steward registra um relacionamento entre duas tabelas remotas (ou entre uma tabela remota e uma tabela local), o relacionamento se torna o caminho de join usado no momento da consulta.
Como o join prevalece. Na compilação da consulta, o Provisa resolve o caminho de join através
do relacionamento registrado. source_column e target_column no relacionamento se tornam a
condição de join no SQL gerado. O join substitui qualquer chamada remota por tabela que de outra
forma seria necessária para o tipo conectado.
O blob bruto nunca é exposto em SQL. A coluna breed em petstore__pets não é selecionável
como um valor jsonb bruto em consultas SQL. Quando um relacionamento é registrado entre
petstore__pets e petstore__breeds, consultas SQL percorrem o join — SELECT breed.name FROM
petstore__pets resolve via o join FK, não um blob. Quando nenhum relacionamento é registrado mas a
coluna tem sub-campos declarados (gql_object_fields), referências de sub-campo SQL são reescritas
para extração ->> contra o blob armazenado. Este caminho está disponível somente para tipos
inline não governados — campos de tipo alvo governado são excluídos inteiramente da SDL e não têm
blob do qual extrair. O blob bruto em si nunca é emitido como um valor de coluna nu. [tool-verified:
provisa/compiler/sql_gen.py:1156, tests/unit/test_sql_gen.py:TestGqlJsonBlobExtraction]
Na SDL GraphQL, um campo OBJECT inline não governado é tipado como o tipo de objeto aninhado. Se é servido por um join ou por extração de blob no momento da execução é um detalhe de implementação — a forma da SDL é idêntica de qualquer forma. Quando o tipo filho é registrado como sua própria tabela (e se torna governado), todas as cinco camadas de governança se aplicam a ele independentemente: suas próprias regras de RLS, visibilidade de coluna, regras de mascaramento, guards de predicado, e controle de acesso a domínio. (REQ-039, REQ-040, REQ-041, REQ-263) A extração de blob ignora isso — os dados do filho chegam pré-embutidos na linha pai e são governados somente pelas regras da tabela pai. Registrar o filho como uma tabela e criar um relacionamento é o caminho para governança de granularidade fina no tipo filho.
graphql_alias no relacionamento. O campo graphql_alias nomeia o campo SDL que o
relacionamento expõe no tipo pai. Quando ausente, o nome é derivado do field_name da tabela alvo e
da cardinalidade do relacionamento via rel_field_name(target.field_name, cardinality). (REQ-605)
[tool-verified: provisa/compiler/schema_gen.py:1050]
V002 no caminho de join. Consultas SQL e Cypher que percorrem o relacionamento estão sujeitas à
governança de relacionamento V002. O relacionamento deve ser registrado e aprovado para que o join
seja permitido. (REQ-604) A travessia GraphQL via campo de relacionamento SDL é sempre
pré-aprovada. [tool-verified: docs/security.md:41–54]
Flag remote-managed. Relacionamentos auto-detectados durante o registro remoto GraphQL são
armazenados com remote_managed: True. (REQ-554) [tool-verified:
provisa/graphql_remote/mapper.py:199] Este é um marcador de metadado; não altera o comportamento
de governança.
Comportamento somente-type-def¶
Nem todo tipo em um esquema remoto precisa ser uma tabela consultável.
Quando root_table_ids é definido em um SchemaInput, tabelas cujos IDs estão ausentes desse
conjunto são excluídas dos campos de consulta raiz na SDL gerada. Elas permanecem presentes como
tipos GraphQL e podem ser alcançadas via campos de relacionamento em tabelas que têm entradas raiz.
(REQ-601) [tool-verified: provisa/compiler/schema_gen.py:1062–1069]
O mesmo mecanismo se aplica a builds de esquema filtrados por domínio: tabelas em domínios que a
função não pode acessar são somente-type-def — sua definição de tipo existe na SDL para travessia
de relacionamento, mas nenhum campo de consulta raiz é gerado para elas. (REQ-039) [tool-verified:
provisa/compiler/schema_gen.py:1068–1076]
Uma tabela somente-type-def:
- Não tem campo de consulta raiz — clientes não conseguem consultá-la diretamente pelo nome.
- É alcançável via campos de relacionamento em tabelas que têm entradas raiz.
- Ainda aparece na introspecção de esquema como um tipo nomeado.
- Ainda tem todas as regras de governança aplicadas quando dados são acessados através de um relacionamento. (REQ-039, REQ-040)
A remoção completa do esquema — incluindo a definição de tipo — só acontece quando o registro da
tabela é excluído inteiramente. Marcar uma tabela como somente-type-def (removendo seu ID de
root_table_ids ou filtrando por acesso a domínio) não remove o tipo.
Este design permite que stewards exponham grafos de objeto navegáveis onde alguns tipos são alcançáveis somente por travessia, não por consulta independente.