Saltar para o conteúdo principal

Ligações

Uma ligação faz uma linha de uma tabela apontar para linhas de outra. Esta página diz como se cria o campo e como se preenche, pela API REST e pelo MCP, e que erros esperar. Os argumentos de cada ferramenta estão em Ferramentas MCP, gerados do código.

O que é uma ligação​

No OutDo uma ligação é uma coluna com o componente linked_select. O config dela diz para onde aponta:

  • linkedFeatureId: a tabela alvo.
  • linkedValueColumn: o id da coluna da tabela alvo cujo valor identifica cada linha. Normalmente é a coluna de componente id.
  • linkedLabelColumn: o id da coluna que a interface mostra.
  • isMultiSelect: true para uma ligação múltipla.

O valor guardado na célula não é o id da linha alvo. É o conteúdo da célula linkedValueColumn dessa linha (por exemplo o data.id gerado à parte). Numa ligação simples a célula guarda uma string; numa múltipla, uma lista de strings. Escrever o id da linha (Featurerow.id) cria uma ligação que nada resolve.

Não precisas de saber isto para ligar: com link_rows dás os ids das linhas alvo e o servidor vai buscar a chave certa a cada uma. O update_row e o PATCH /rows continuam a aceitar o valor cru, para quem já o tiver.

A coluna inversa de uma ligação (linked_back) vive na tabela alvo, mostra as linhas que apontam para ela e partilha com a ligação um mesmo relationId. Não guarda dados: calcula-se a partir das ligações.

Escreve na célula de ligação de uma linha, com os ids das linhas alvo.

ArgumentoSignificado
feature_idA tabela da linha de origem.
row_idA linha de origem.
columnA coluna de ligação, pelo nome ou pelo id.
target_row_idsOs ids (Featurerow.id) das linhas alvo. No máximo 100; repetidos contam uma vez.
modeset (omissão), add ou remove.
idempotency_keyOpcional. Torna a repetição segura.

Os três modos​

  • set: a célula passa a ser exatamente as chaves pedidas. Uma lista vazia limpa a célula (a chave desaparece da linha).
  • add: junta as chaves às que já lá estão, sem repetir, com as existentes primeiro.
  • remove: tira as chaves pedidas; as que não lá estavam são ignoradas. Se a célula ficar vazia, é limpa.

Simples e múltipla​

Numa ligação simples (isMultiSelect falso) cabe um só registo. set aceita zero ou um id; add aceita um id e substitui o valor atual; remove só limpa se a chave pedida for a que lá está. Mais de um id dá 422 single_link_column.

Numa ligação múltipla a célula é uma lista, e os três modos funcionam como descritos.

Exemplo no MCP​

Ligar a linha de uma encomenda a dois clientes, juntando-os aos que já tinha:

{
"name": "link_rows",
"arguments": {
"feature_id": "6f1c0a52-3b7e-4c1d-9a2e-5d8b7c4e1a10",
"row_id": "0b9d2f64-8a31-4f7c-b5e0-2c6a91d3e4f8",
"column": "clientes",
"target_row_ids": [
"c3a8e1d0-5b42-4a96-8f17-9e0d6b2a7c54",
"e7f4b9a2-1c68-4d35-a0b3-6f2e8d1c9a07"
],
"mode": "add"
}
}

A resposta traz a linha atualizada em result.

Equivalente REST​

POST /v1/features/{feature_id}/rows/{row_id}:link
Authorization: Bearer outdo_sk_...
Content-Type: application/json

{
"column": "clientes",
"target_row_ids": [
"c3a8e1d0-5b42-4a96-8f17-9e0d6b2a7c54",
"e7f4b9a2-1c68-4d35-a0b3-6f2e8d1c9a07"
],
"mode": "add"
}

Responde 200 com a linha. Um Idempotency-Key no cabeçalho torna a repetição segura. Precisa do scope write e da permissão de editar registos na tabela de origem; as linhas alvo só têm de estar visíveis para quem chama.

O que o servidor faz por ti​

  1. Confirma que a coluna é linked_select e lê o config dela.
  2. Lê as colunas da tabela alvo e descobre o nome da linkedValueColumn.
  3. Lê as linhas alvo pedidas. Se alguma não existir ou não estiver visível, a chamada falha e a resposta lista os ids em falta.
  4. Para cada linha usa data[nome da linkedValueColumn] como chave. Uma célula vazia nunca é substituída pelo id da linha: é um erro.
  5. Aplica o mode ao valor atual e grava só essa célula, com validação.

Cria a coluna de ligação numa tabela, apontada para outra do mesmo workspace, com o config completo. Sem esta ferramenta, escrever o config à mão obriga a conhecer ids de colunas e a criar a inversa por fora.

ArgumentoSignificado
feature_idA tabela de origem, onde nasce a coluna.
nameO nome da coluna.
target_feature_idA tabela alvo.
labelO rótulo. Por omissão, o name.
multitrue para ligação múltipla. Por omissão, false.
label_columnO nome de uma coluna da tabela alvo, para a interface mostrar.
back_linktrue cria também a coluna inversa na tabela alvo.
back_link_labelO rótulo da inversa.
required, hidden, orderComo em qualquer coluna.
idempotency_keyOpcional.

O que o servidor escolhe​

  • linkedValueColumn: a coluna de componente id da tabela alvo. Sem ela, 422 target_without_id_column.
  • linkedLabelColumn: a coluna pedida em label_column; se faltar, a primeira coluna da tabela alvo (por ordem) que seja visível, de texto, e não seja id, linked_back, lookup, rollup nem formula; se não houver nenhuma, a própria coluna id. A escolha automática pode não ser a que querias: corrige-a com update_column.
  • O resto do config fica com as omissões da interface.

Sem inversa​

{
"name": "create_link_column",
"arguments": {
"feature_id": "6f1c0a52-3b7e-4c1d-9a2e-5d8b7c4e1a10",
"name": "clientes",
"label": "Clientes",
"target_feature_id": "a2d5c8f1-7e30-4b94-86c2-1f9a0e3d5b77",
"multi": true
}
}

A resposta traz column, a coluna criada. Depois preenche-a com link_rows.

Com inversa​

Com back_link a tabela alvo ganha uma coluna linked_back, e as duas colunas partilham um relationId novo:

{
"name": "create_link_column",
"arguments": {
"feature_id": "6f1c0a52-3b7e-4c1d-9a2e-5d8b7c4e1a10",
"name": "clientes",
"target_feature_id": "a2d5c8f1-7e30-4b94-86c2-1f9a0e3d5b77",
"multi": true,
"back_link": true,
"back_link_label": "Encomendas"
}
}

Sem back_link_label a inversa chama-se «rótulo da origem + ligados» (por exemplo, Encomendas ligados). A resposta traz column e back_link_column. Criar a inversa mexe na estrutura da tabela alvo, por isso exige permissão de gerir estrutura nas duas tabelas.

Se a inversa não puder ser criada, o servidor apaga a coluna de origem e responde 502 back_link_failed. Se também não a conseguir apagar, responde 502 back_link_failed_forward_kept com o forward_column_id da coluna que ficou, para a apagares com delete_column.

Equivalente REST​

POST /v1/features/{feature_id}/columns:link
Authorization: Bearer outdo_sk_...
Content-Type: application/json

{
"name": "clientes",
"target_feature_id": "a2d5c8f1-7e30-4b94-86c2-1f9a0e3d5b77",
"multi": true,
"back_link": { "label": "Encomendas" }
}

Responde 201 com {column, back_link_column}. No REST, back_link é um objeto ({} pede a inversa com o rótulo por omissão). No MCP são os dois argumentos back_link e back_link_label.

Scopes​

OperaçãoScope da chave
link_rowswrite
create_link_column, update_column, delete_columnwrite e structure (o structure é aditivo ao write)

Os scopes só limitam: a permissão do utilizador por trás da chave continua a decidir, tabela a tabela.

Alterar e apagar o campo​

update_column (PATCH /v1/features/{feature_id}/columns/{column_id}) muda só os campos que enviares. Atenção: o config, quando vai, substitui o config inteiro. Para mudar, por exemplo, a linkedLabelColumn, lê o config com list_columns, altera essa chave e devolve-o completo, com o linkedFeatureId e as outras chaves intactas.

delete_column (DELETE .../columns/{column_id}) apaga a definição da coluna. Os valores já guardados nas linhas ficam como chaves órfãs. Apagar a coluna de ligação não apaga a inversa na tabela alvo: apaga-a tu, com o id dela.

Erros​

Os erros das rotas :link trazem um code estável no corpo do problema, repetido em detail.code. As ferramentas MCP devolvem o mesmo code na mensagem.

EstadocodeQuando
422not_a_link_columnA coluna dada a link_rows não é linked_select.
422unsupported_componentA coluna é auto_match, que a API não liga.
422single_link_columnMais de um id numa ligação simples.
422cross_workspace_linkAs duas tabelas são de workspaces diferentes.
422target_without_id_columnA tabela alvo não tem coluna de componente id.
404target_rows_not_foundHá linhas alvo inexistentes ou invisíveis; detail.missing lista os ids.
404target_feature_not_foundA tabela alvo não existe ou não está visível.
404label_column_not_foundO label_column pedido não é uma coluna da tabela alvo.
409link_column_misconfiguredO config da coluna não tem linkedFeatureId ou linkedValueColumn válidos, ou a coluna da chave já não existe na alvo.
409link_key_missingUma linha alvo tem a célula da chave vazia; detail.row_id diz qual.
502back_link_failedA inversa falhou; a coluna de origem foi removida.
502back_link_failed_forward_keptA inversa falhou e a coluna de origem ficou; detail.forward_column_id diz qual.

Há ainda os erros comuns: 404 not_found para uma coluna ou uma linha de origem que não existe, 403 sem a permissão ou o scope, e 422 do próprio corpo (um id que não é uuid, ou mais de 100 em target_row_ids).

Limitações​

  • Concorrência em add e remove. O servidor lê a célula e depois escreve a nova: as duas operações não são atómicas. Duas chamadas simultâneas sobre a mesma célula podem perder uma das escritas. É a mesma limitação do PATCH de linhas. Serializa as chamadas sobre a mesma célula, ou usa set com a lista completa.
  • Dados antigos inválidos. A escrita valida a linha inteira: uma linha com um valor inválido noutra coluna recusa a ligação até se corrigir.
  • Campos obrigatórios. Limpar uma ligação required com set e lista vazia dá 422, como qualquer outra escrita que a esvazie.

O que fica fora​

  • auto_match. Não se liga por esta via (422 unsupported_component).
  • Integridade referencial. O servidor não impede apagar uma linha que outras apontam, nem limpa as ligações órfãs: isso continua a ser feito pela aplicação.
  • Ligações entre workspaces. Recusadas (422 cross_workspace_link).