Перейти к содержанию

Удалённые схемы (Remote Schemas)

Источник удалённой схемы подключает внешний API — GraphQL, gRPC или REST (OpenAPI) — к семантическому слою Provisa. После регистрации операции внешнего API становятся полноценными таблицами и функциями Provisa. (REQ-308, REQ-316, REQ-325) Каждое правило governance, интерфейс запросов и слой безопасности применяются автоматически. (REQ-310, REQ-319, REQ-328) Удалённый сервис никогда не видит правил governance Provisa. (REQ-310, REQ-319, REQ-328)


Три типа источников

Удалённая схема GraphQL (REQ-307–313)

Как зарегистрировать. Отправьте POST на /admin/sources/graphql-remote с URL эндпоинта, пространством имён и опциональной аутентификацией. Provisa выполняет стандартный запрос интроспекции __schema к удалённому эндпоинту. (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" }
  ]
}

Варианты аутентификации: none, bearer (заголовок Authorization), basic (Base64 username:password). (REQ-307) [tool-verified: provisa/graphql_remote/introspect.py:36–45]

Переопределения полей. field_overrides — это карта {fieldName: "query" | "mutation"}, применяемая после интроспекции. Она имеет приоритет над структурной классификацией. Только поля типа query могут быть переклассифицированы как мутации; поля типа mutation не имеют пути переопределения в GraphQL. (REQ-531) [tool-verified: provisa/graphql_remote/mapper.py]

Связи на момент регистрации. relationships объявляет пути соединения FK/PK между таблицами на момент регистрации. Они сохраняются как связи, объявленные вручную (без флага remote_managed). При обновлении автоматически обнаруженные связи (те, что с remote_managed: True) выполняются заново и могут измениться; вручную объявленные связи не затрагиваются. (REQ-554) [tool-verified: provisa/api/admin/graphql_remote_router.py]

Что обнаруживается автоматически. Каждое поле удалённого типа Query, возвращающее OBJECT, становится виртуальной таблицей. Каждое поле удалённого типа Mutation становится отслеживаемой функцией. (REQ-308) [tool-verified: provisa/graphql_remote/mapper.py:243–278]

Именование таблиц. Таблицы называются {namespace}__{field_name}. С пространством имён petstore и полем запроса pets: имя таблицы — petstore__pets. (REQ-312) [tool-verified: provisa/graphql_remote/mapper.py:250]

Отображение типов (REQ-308). Скалярные поля отображаются на типы Provisa напрямую. Поля OBJECT разделяются на два случая в зависимости от того, управляется ли целевой тип (см. «Управляемые таблицы» ниже). [tool-verified: provisa/graphql_remote/mapper.py:14–36, provisa/api/data/endpoint.py:655–671, provisa/compiler/schema_gen.py:481–485]

Тип GraphQL Тип Provisa
String text
ID text
Int integer
Float numeric
Boolean boolean
OBJECT (неуправляемый встроенный тип, например ContactInfo) столбец-блоб jsonb
OBJECT (управляемый целевой тип) полностью исключается из SDL и выборки
Любой ENUM jsonb
Пользовательский скаляр text (запасной вариант)

Управляемые таблицы. Тип GQL управляется, когда он появляется как корневое поле Query в удалённой схеме. _collect_queryable_types собирает их во время регистрации, предпочитая поля без обязательных аргументов, чтобы их можно было массово выбирать как цели соединений. [tool-verified: provisa/graphql_remote/mapper.py:395–413]

Когда столбец типа OBJECT на управляемой таблице указывает на другой управляемый тип, этот столбец подчиняется трём правилам одновременно [tool-verified: provisa/api/data/endpoint.py:655–671, provisa/compiler/schema_gen.py:481–485]:

  1. Исключён из выборки GQL — поле не запрашивается при получении строк родительской таблицы.
  2. Исключён из SDL — поле не появляется на родительском типе в сгенерированной схеме.
  3. Доступен только через объявленную связь — куратор должен зарегистрировать JOIN между двумя материализованными управляемыми таблицами. Без этого поле просто отсутствует; запасного блоба нет.

Типы OBJECT, недостижимые как корневые поля Query (встроенные типы, такие как ContactInfo или Address), следуют другим правилам: они выбираются как столбцы-блобы jsonb и появляются в SDL как поля вложенных объектов. Доступ к подполям осуществляется через извлечение -->> в SQL.

Обязательные аргументы. Когда корневое поле запроса имеет не-null аргументы без значения по умолчанию, они становятся столбцами native_filter_type: query_param на таблице (с префиксом _nf_ на момент внедрения). Исполнитель передаёт их как переменные GraphQL. (REQ-555) [tool-verified: provisa/graphql_remote/mapper.py:110–120, provisa/api/app.py:1280–1303]

Связи, обнаруживаемые автоматически. Provisa сканирует столбцы типа OBJECT каждой таблицы. Когда ссылаемый тип GQL также зарегистрирован как таблица в том же источнике, связь выдаётся. Связи many-to-one выводят исходный и целевой столбцы из соглашений об именовании (breedName на исходном типе → name на целевом типе Breed). Поля one-to-many (LIST) выдают связи с пустыми ссылками на столбцы — FK находится на стороне цели. (REQ-554) [tool-verified: provisa/graphql_remote/mapper.py:162–202]

Мутации. Поля мутаций производят отслеживаемые функции с типами аргументов, отображёнными из аргументов мутации, и return_schema, выведенной из возвращаемого типа мутации. (REQ-308) [tool-verified: provisa/graphql_remote/mapper.py:261–278]

Обновление. Отправьте POST на /admin/sources/graphql-remote/{id}/refresh. Повторно интроспектирует удалённую схему и обновляет регистрации таблиц и функций. Существующие правила governance (RLS, маскирование) сохраняются. (REQ-311) [tool-verified: provisa/api/admin/graphql_remote_router.py:217–257]

Ограничения.

  • Скалярные и ENUM корневые поля запроса (тип возврата не OBJECT) становятся отслеживаемыми функциями, а не виртуальными таблицами. Их return_schema — это единственный столбец value отображённого скалярного типа. [tool-verified: provisa/graphql_remote/mapper.py:254–279]
  • Вложенность объектов разрешается на момент регистрации до graphql_remote.max_object_depth (по умолчанию: 5). И выборка удалённого запроса, и метаданные подполей строятся до этой глубины; поля за пределами лимита не выбираются и недоступны для извлечения в SQL. (REQ-556) [tool-verified: provisa/graphql_remote/mapper.py:38–52]
  • Вложенные поля OBJECT типа LIST (например, breed.awards: [Award]) включаются в выборку запроса до уровней вложенности graphql_remote.max_list_depth (по умолчанию: 2). В пределах этого лимита список выбирается как массив jsonb в родительском столбце, и выборка GQL внедряет first: N, где N — это graphql_remote.max_list_items (по умолчанию: 100), чтобы ограничить размер массива. За пределами max_list_depth поле LIST полностью исключается для предотвращения неограниченного расширения данных. В SQL доступ к массиву осуществляется через json_array_elements(column_name) или извлечение по индексу ->>. Если тип элемента списка имеет свой собственный корневой запрос, зарегистрируйте его как отдельную таблицу и создайте связь вместо этого — путь соединения эффективнее и обходит блоб. (REQ-556) [tool-verified: provisa/graphql_remote/mapper.py:43–70]
  • Для SQL-запросов неуправляемые столбцы типа OBJECT выбираются полностью из удалённого источника (все подполя до настроенной глубины) и кешируются как jsonb. Доступ к подполям в SQL обрабатывается через извлечение ->> из блоба; удалённый запрос не сужается только до полей, которые выбирает SQL-запрос. Когда тип элемента LIST не имеет корневого запроса, а представление блоба недостаточно, пишите запрос напрямую в GraphQL SDL — Provisa точно воспроизводит выборку полей GQL, так что удалённая сторона видит ровно запрошенные поля. [tool-verified: provisa/compiler/sql_gen.py:1332–1368]
  • Если удалённый сервер отклоняет поле типа OBJECT, потому что оно требует выбора подполей (что не должно происходить, когда доступна gql_selection), исполнитель повторяет попытку один раз с удалением этих полей, чтобы скалярные столбцы всё же вернулись. [tool-verified: provisa/graphql_remote/executor.py:76–80]

Удалённая схема gRPC (REQ-322–329)

Как зарегистрировать. Отправьте POST на /admin/grpc-remote/register с адресом сервера, путём или URL к файлу .proto и опциональной конфигурацией TLS.

{
  "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" }
  ]
}

Provisa получает proto, разбирает его чисто текстовым парсером (без внешних зависимостей proto на этапе разбора), компилирует Python-заглушки через grpc_tools.protoc и открывает постоянный grpc.aio.Channel. (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]

Файлы proto также могут быть локальными путями. Пути импорта для хорошо известных типов (google/protobuf/timestamp.proto) сохраняются на момент регистрации и переиспользуются при обновлении. (REQ-329) [tool-verified: provisa/grpc_remote/loader.py:135–159]

Что обнаруживается автоматически. Каждый метод rpc в proto классифицируется как query или mutation, используя три сигнала в порядке приоритета: (REQ-323) [tool-verified: provisa/grpc_remote/mapper.py]

  1. method_overrides в полезной нагрузке регистрации — {"MethodName": "query"} или {"MethodName": "mutation"} переопределяет всё остальное.
  2. server_streaming: true — сервер отправляет поток сообщений; всегда виртуальная таблица (если только вывод не скаляр).
  3. Выходное сообщение имеет повторяющееся поле типа message — например, ListOrdersResponse { repeated Order items; } рассматривается как обёртка списка и становится виртуальной таблицей. Повторяющиеся скалярные поля (например, repeated string tags) это не вызывают — они являются свойствами-массивами одной сущности, а не источниками строк.

Методы, не соответствующие ни одному из этих сигналов (унарный RPC, возвращающий одно сообщение-сущность, или любой скалярный вывод), становятся отслеживаемыми функциями.

Именование таблиц. Имя по умолчанию — {namespace}__{ServiceName}__{MethodName}. Без пространства имён имена сервиса и метода соединяются напрямую. Любой зарегистрированной таблице можно задать alias; когда он установлен, алиас — это имя, используемое повсюду (запросы, SDL, связи). Автоматически сгенерированное имя — это ключ регистрации, который никогда не меняется. (REQ-322) [tool-verified: provisa/core/repositories/table.py:129–134]

Отображение типов (REQ-324). Скалярные типы proto отображаются на типы SQL следующим образом. [tool-verified: provisa/grpc_remote/mapper.py:31–47]

Тип Proto Тип 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
Вложенное message jsonb
Enum text

Связи на момент регистрации. relationships работает идентично адаптеру GQL — объявляет пути соединения FK/PK, сохраняемые как связи, объявленные вручную (без флага remote_managed). При обновлении они остаются без изменений. (REQ-554) [tool-verified: provisa/api/admin/grpc_remote_router.py:93–109]

Методы Query (REQ-325). Поля выходного сообщения становятся столбцами таблицы. Поля входного сообщения одновременно становятся аргументами GraphQL, передаваемыми в удалённый вызов, и регистрируются как столбцы с префиксом _nf_ и native_filter_type: "grpc_input" — тот же механизм, что используют GQL и OpenAPI для внедрения нативных фильтров. (REQ-555) [tool-verified: provisa/api/admin/grpc_remote_router.py:207–213]

Подполя вложенного message. Для методов query неповторяющиеся поля типа message на глубине 0 (прямые выходные столбцы) имеют свои подполя, разрешённые на один уровень вглубь и сохранённые как object_fields на ColumnDef. Эти метаданные используются для извлечения подполей jsonb в SQL и для документации схемы. Поля, вложенные глубже уровня 1, не раскрываются рекурсивно. (REQ-556) [tool-verified: provisa/grpc_remote/mapper.py:111–128]

Методы с серверным стримингом собирают все переданные потоком сообщения в список перед возвратом строк. (REQ-325) [tool-verified: provisa/grpc_remote/executor.py:86–119]

Методы Mutation (REQ-326). Поля входного сообщения становятся аргументами ввода мутации. Схема выходного сообщения становится return_schema. [tool-verified: provisa/grpc_remote/executor.py:122–143]

Управление каналами. Один grpc.aio.Channel на зарегистрированный источник хранится в состоянии приложения и переиспользуется между запросами. Старый канал закрывается до открытия нового при обновлении. (REQ-327) [tool-verified: provisa/api/admin/grpc_remote_router.py:107–117]

Обновление. Отправьте POST на /admin/grpc-remote/refresh/{source_id}. Заново загружает proto из сохранённого пути, перекомпилирует заглушки и заново регистрирует таблицы и функции. Также можно отправить PUT на /admin/grpc-remote/{source_id}/proto с новым proto_text, чтобы обновить proto инлайн. (REQ-329) [tool-verified: provisa/api/admin/grpc_remote_router.py:241–268, provisa/api/admin/grpc_remote_router.py:300–358]

Ограничения.

  • Извлечение подполей объекта — на один уровень вглубь. Поля вложенного message глубже уровня 1 не раскрываются рекурсивно. (REQ-556) [tool-verified: provisa/grpc_remote/mapper.py:111–128]

OpenAPI / REST (REQ-314–321)

Как зарегистрировать. Вызовите auto_register_openapi_source с ID источника, разобранной спецификацией и метаданными подключения. Спецификация загружается из локального файла или URL. (REQ-314) [tool-verified: provisa/openapi/loader.py:30–55, provisa/openapi/register.py:249–264]

Полезная нагрузка регистрации. Эндпоинт /admin/openapi/register принимает два дополнительных поля наряду с source_id, spec_path и т. д.:

{
  "operation_overrides": { "createPet": "query", "listOrders": "mutation" },
  "relationships": [
    { "source_table": "pets__listPets", "source_column": "owner_id",
      "target_table": "owners__listOwners", "target_column": "id" }
  ]
}

Что обнаруживается автоматически. Каждая операция GET в спецификации становится виртуальной таблицей, если только её схема ответа не является скалярным типом (string, number, boolean, integer) — GET-операции, возвращающие скаляр, вместо этого становятся отслеживаемыми функциями с единственным столбцом value. Каждая операция, отличная от GET (POST, PUT, PATCH, DELETE), становится отслеживаемой функцией. (REQ-316, REQ-317)

Приоритет классификации: operation_overrides (полезная нагрузка) переопределяет x-provisa-kind (расширение спецификации), которое переопределяет эвристику GET. operation_overrides — рекомендуемый путь переопределения; x-provisa-kind — для случаев, когда сама спецификация должна нести классификацию. (REQ-408) [tool-verified: provisa/openapi/mapper.py:192–203]

Связи на момент регистрации. relationships работает идентично другим адаптерам — сохраняется как связи, объявленные вручную, и сохраняется при обновлении. (REQ-554) [tool-verified: provisa/api/admin/openapi_router.py:103–108]

Именование таблиц. Таблицы используют operationId операции. Если operationId не определён, Provisa образует слаг {method}_{path}. Алиас выводится удалением ведущего сегмента глагола и приведением существительного к единственному числу (findPetsByStatuspet_by_status). (REQ-557) [tool-verified: provisa/openapi/register.py:39–56]

Отображение типов. Типы JSON Schema отображаются на типы Provisa следующим образом. [tool-verified: provisa/openapi/register.py:59–70]

Тип JSON Schema Тип Provisa
string string
integer integer
number number
boolean boolean
array jsonb
object jsonb

Параметры как столбцы нативного фильтра. Параметры пути и запроса, ещё не являющиеся полями ответа, становятся столбцами с native_filter_type, установленным в path_param или query_param, с префиксом _nf_. Когда имя параметра совпадает с именем поля ответа, метаданные параметра объединяются в существующую запись столбца, а не создают дубликат. (REQ-555) [tool-verified: provisa/openapi/register.py:116–122, provisa/openapi/register.py:172–196]

Разрешение схемы ответа. Маппер проверяет responses.200, затем responses.2xx, затем responses.default. Ответы типа массив разворачиваются в схему их элемента. Ссылки $ref разрешаются на один уровень вглубь. (REQ-316) [tool-verified: provisa/openapi/mapper.py:83–101]

Подполя объекта. Свойства ответа с type: object и собственными properties сохраняются как object_fields на столбце. Эти подполя видны в SDL и используются для извлечения jsonb в запросах. (REQ-556) [tool-verified: provisa/openapi/register.py:87–96]

Кеширование ответов (REQ-318). Результаты операций GET кешируются в PostgreSQL через pg_cache.py. Каждая комбинация параметров запроса получает собственную группу _params_hash. Строки для данного хеша заменяются при истечении TTL. Эндпоинты с параметрами пути (/pets/{id}) пропускают первоначальную массовую выборку — таблица кеша создаётся пустой для интроспекции схемы, затем заполняется по PK по мере поступления запросов. [tool-verified: provisa/openapi/pg_cache.py:181–234, provisa/openapi/pg_cache.py:307–360]

Обновление (REQ-321). Заново разберите спецификацию и снова вызовите auto_register_openapi_source. Существующие правила governance сохраняются; регистрации обновляются через upsert ON CONFLICT. [tool-verified: provisa/openapi/register.py:249–264]

Ограничения.

  • Извлечение подполей объекта — на один уровень вглубь. Свойства, вложенные внутри object_fields, не раскрываются рекурсивно. (REQ-556) [tool-verified: provisa/openapi/register.py:87–96]
  • Параметры заголовков и cookie игнорируются; регистрируются только параметры path и query. (REQ-555) [tool-verified: provisa/openapi/mapper.py:144–158]
  • Разрешение $ref на уровне спецификации — на один уровень вглубь для схем свойств; глубоко вложенные ссылки на компоненты могут не разрешиться. [tool-verified: provisa/openapi/mapper.py:51–60]

Влияние регистрации удалённой таблицы

Таблица, зарегистрированная из любого источника удалённой схемы, — это полноценная таблица Provisa. Во время выполнения она ничем не отличается от локально подключённой реляционной таблицы. (REQ-308, REQ-313)

Интерфейсы запросов. Таблица немедленно доступна для запросов через GraphQL, SQL (pgwire или прямой), Cypher (GQL), JSON:API и Arrow Flight. (REQ-001, REQ-267, REQ-345, REQ-257, REQ-051) Генерация схемы синтезирует ColumnMetadata для удалённых таблиц, поскольку у них нет каталога — отображение типов применяется на этапе построения схемы. (REQ-602) [tool-verified: provisa/api/app.py:1367–1386]

Модель безопасности. Применяются все пять слоёв governance:

  1. Контроль доступа к домену — domain_id таблицы ограничивает, какие роли могут её видеть. (REQ-039) [tool-verified: provisa/compiler/schema_gen.py:1064–1076]
  2. Безопасность на уровне строк (RLS) — фильтры строк, настроенные на таблице, внедряются в каждый запрос независимо от интерфейса. (REQ-040, REQ-041)
  3. Видимость столбцов — список visible_to на каждом столбце управляет раскрытием поля по ролям. (REQ-039)
  4. Маскирование столбцов — правила маскирования применяются на Этапе 2 конвейера governance. (REQ-040, REQ-263)
  5. Защита предикатов — замаскированные столбцы отклоняются в предложениях WHERE и HAVING. (REQ-603)

Произвольные запросы к удалённым таблицам разрешены исключительно в рамках прав пользователя — доступ единообразно основан на правах (права на таблицу/столбец + одобренные связи), без режима governance для каждой таблицы. (REQ-001, REQ-003)

Governance связей (V002). Условия JOIN к удалённым таблицам — при запросе через SQL или Cypher — должны соответствовать зарегистрированной, одобренной связи. (REQ-604) Проверка V002 пропускается для запросов GraphQL, потому что связи, определённые в SDL, изначально одобрены по конструкции. См. docs/security.md.

Столбцы типа OBJECT. Когда столбец отображается на неуправляемый встроенный GQL OBJECT или объектный тип OpenAPI, его тип Provisa — jsonb. Столбец хранит полный вложенный блоб JSON. Когда подполя объявлены (gql_object_fields или object_fields), карта gql_object_columns заполняется на этапе построения схемы. Генератор SQL использует эту карту для выдачи выражений извлечения ->> для подполей, когда запрос их выбирает. [tool-verified: provisa/api/app.py:1305–1315, provisa/compiler/schema_gen.py:80–82]

Обязательные аргументы как параметры нативного фильтра. Корневые поля запроса с не-null аргументами без значения по умолчанию внедряют дополнительные столбцы в зарегистрированную таблицу. Эти столбцы несут native_filter_type: query_param. Транслятор Cypher переписывает WHERE n.id = $val в WHERE n._nf_id = $val, а исполнитель GraphQL подбирает их как переменные для передачи удалённому эндпоинту. (REQ-555) [tool-verified: provisa/api/app.py:1280–1303]


Влияние создания покрывающей связи

Когда куратор регистрирует связь между двумя удалёнными таблицами (или между удалённой таблицей и локальной таблицей), связь становится путём соединения, используемым во время выполнения запроса.

Как побеждает соединение. На этапе компиляции запроса Provisa разрешает путь соединения через зарегистрированную связь. source_column и target_column на связи становятся условием соединения в сгенерированном SQL. Соединение заменяет любой удалённый вызов на таблицу, который иначе потребовался бы для связанного типа.

Сырой блоб никогда не раскрывается в SQL. Столбец breed на petstore__pets не выбираем как сырое значение jsonb в SQL-запросах. Когда между petstore__pets и petstore__breeds зарегистрирована связь, SQL-запросы проходят через соединение — SELECT breed.name FROM petstore__pets разрешается через соединение по FK, а не блоб. Когда связь не зарегистрирована, но столбец имеет объявленные подполя (gql_object_fields), ссылки на подполя в SQL переписываются в извлечение ->> из хранимого блоба. Этот путь доступен только для неуправляемых встроенных типов — поля, ссылающиеся на управляемые типы, полностью исключены из SDL, и у них нет блоба для извлечения. Сам сырой блоб никогда не выдаётся как значение голого столбца. [tool-verified: provisa/compiler/sql_gen.py:1156, tests/unit/test_sql_gen.py:TestGqlJsonBlobExtraction]

В SDL GraphQL неуправляемое встроенное поле OBJECT типизировано как вложенный тип объекта. Обслуживается ли оно соединением или извлечением блоба во время выполнения — это деталь реализации; форма SDL идентична в обоих случаях. Когда дочерний тип зарегистрирован как собственная таблица (и становится управляемым), все пять слоёв governance применяются к нему независимо: его собственные правила RLS, видимость столбцов, правила маскирования, защита предикатов и контроль доступа к домену. (REQ-039, REQ-040, REQ-041, REQ-263) Извлечение блоба это обходит — дочерние данные прибывают предварительно встроенными в родительскую строку и управляются только правилами родительской таблицы. Регистрация дочернего элемента как таблицы и создание связи — это путь к точному governance на дочернем типе.

graphql_alias на связи. Поле graphql_alias называет поле SDL, которое связь раскрывает на родительском типе. Когда оно отсутствует, имя выводится из field_name целевой таблицы и кардинальности связи через rel_field_name(target.field_name, cardinality). (REQ-605) [tool-verified: provisa/compiler/schema_gen.py:1050]

V002 на пути соединения. Запросы SQL и Cypher, проходящие через связь, подчиняются governance связей V002. Связь должна быть зарегистрирована и одобрена, чтобы соединение было разрешено. (REQ-604) Обход через поле связи SDL в GraphQL всегда предварительно одобрен. [tool-verified: docs/security.md:41–54]

Флаг remote-managed. Связи, автоматически обнаруженные во время регистрации удалённой схемы GraphQL, сохраняются с remote_managed: True. (REQ-554) [tool-verified: provisa/graphql_remote/mapper.py:199] Это метаданный-маркер; он не изменяет поведение governance.


Поведение только-определение-типа (type-def-only)

Не каждый тип в удалённой схеме должен быть таблицей, доступной для запросов.

Когда на SchemaInput установлен root_table_ids, таблицы, чьи ID отсутствуют в этом наборе, исключаются из корневых полей запроса в сгенерированном SDL. Они остаются присутствующими как типы GraphQL и достижимы через поля связей на таблицах, которые действительно имеют корневые записи. (REQ-601) [tool-verified: provisa/compiler/schema_gen.py:1062–1069]

Тот же механизм применяется к сборкам схемы, отфильтрованным по домену: таблицы в доменах, к которым роль не имеет доступа, являются только определениями типов — их определение типа существует в SDL для обхода связей, но корневое поле запроса для них не генерируется. (REQ-039) [tool-verified: provisa/compiler/schema_gen.py:1068–1076]

Таблица только-определение-типа:

  • Не имеет корневого поля запроса — клиенты не могут запросить её напрямую по имени.
  • Достижима через поля связей на таблицах, которые имеют корневые записи.
  • По-прежнему появляется в интроспекции схемы как именованный тип.
  • По-прежнему имеет все правила governance, применяемые при доступе к данным через связь. (REQ-039, REQ-040)

Полное удаление из схемы — включая определение типа — происходит только когда регистрация таблицы удаляется полностью. Пометка таблицы как только-определение-типа (удалением её ID из root_table_ids или фильтрацией по доступу к домену) не удаляет тип.

Этот дизайн позволяет кураторам предоставлять доступ к навигируемым графам объектов, где некоторые типы достижимы только через обход, а не через независимый запрос.