Como um conector funciona
Esta parte é para quem quer entender, revisar ou depurar o que a IA escreve. Conectores são escritos pela IA de construção ou, por quem tem TI própria, no próprio editor com a CLI.
Arquivo e manifesto
Seção intitulada “Arquivo e manifesto”Um conector é:
- um arquivo TypeScript que exporta como
defaultuma função que recebe octx; - um manifesto JSON que declara o cron, os hosts, as rotas, os agentes, os segredos e se o conector escreve fora.
O código é compilado num arquivo só. Imports só do Node ou de tipos; não há pacotes de terceiros, rede, sistema de arquivos útil nem variáveis de ambiente. Tudo o que sai do código passa pelo ctx.
Fatias e cursor
Seção intitulada “Fatias e cursor”Uma execução roda em fatias. Em cada fatia a função é chamada de novo, do começo, com o mesmo ctx.state. O padrão é sempre o mesmo:
- Ler o cursor de
ctx.state(a data da última alteração lida, a última chave). - Ler uma página da origem a partir do cursor.
- Processar e gravar o novo cursor com
ctx.state.set. - Se
ctx.shouldYield()fortrue, devolver{ done: false }para continuar na próxima fatia; quando não houver mais nada, devolver{ done: true }(ou nada).
O ctx.state sobrevive entre execuções: a próxima execução começa de onde a última que terminou bem (sucesso ou parcial) parou. Uma execução que falha, ou uma execução real com escrita que ficou guardada esperando confirmação, não avança o cursor, então nada se perde. Zere no estado o que só serve a uma execução (o número da página, por exemplo).
Ler e escrever
Seção intitulada “Ler e escrever”| Chamada | Uso | No ensaio | Em produção |
|---|---|---|---|
ctx.http.request |
Ler, com qualquer método (um POST que só consulta vai aqui). |
De verdade | De verdade |
ctx.http.write |
Criar, alterar ou apagar algo em outro sistema. | Registrada, não sai (planned) |
Exige externalWrites; a primeira em cada host espera confirmação |
client.read.<Op> (SOAP) |
Operação SOAP que só consulta. | De verdade | De verdade |
client.write.<Op> (SOAP) |
Operação SOAP que muda algo. | Registrada, não sai | Como http.write |
ctx.db.query |
Um SELECT num banco da rede da loja, pelo agente. |
De verdade | De verdade |
Uma escrita feita por ctx.http.request passaria no ensaio como leitura: por isso o ensaio lista todo POST, PUT, PATCH e DELETE feito por request (writesAsReads) para uma pessoa conferir. Toda escrita na Zoppy usa write.
Quando planned vem true, a escrita não chegou ao outro sistema (ensaio, ou a primeira escrita esperando confirmação). O código deve seguir normalmente, sem tratar como erro.
Toda escrita é idempotente
Seção intitulada “Toda escrita é idempotente”O gateway não deduplica: quem garante que nada se repete é o conector. Toda escrita, na Zoppy ou em outro sistema, é:
- um upsert pela chave natural (id externo, documento, e-mail ou telefone), com
PUTouPATCHno id do registro; ou - uma criação (
POST) feita depois de consultar, na mesma execução, se o registro já existe: umGETpela chave, ou a rota de leitura declarada emreadRoutes, na mesma coleção.
Criar sem essa consulta é proibido, porque o cursor é relido: depois que uma escrita guardada é confirmada, depois de uma queda e no reprocesso da quarentena, o mesmo registro passa de novo pelo conector.
O ensaio confere a regra. Um POST que é escrita (fora de readRoutes) num caminho sem nenhuma leitura antes, na mesma execução, no mesmo host e na mesma coleção (o caminho sem os ids do fim: /customers/123 e /customers são a mesma coleção), volta como problema connect.write_without_lookup. Esse ensaio não conta como limpo e a publicação é recusada com 409 connect.write_without_lookup até um ensaio sem ele. PUT e PATCH contam como upsert e não são cobrados.
Registro recusado: quarentena
Seção intitulada “Registro recusado: quarentena”Um registro que o outro lado recusa com 4xx, ou que não dá para mapear, vai para ctx.quarantine(registro, motivo) e o conector segue com o próximo. Falha geral (o sistema fora do ar, credencial errada) é erro: deixe a exceção subir para a execução falhar e a próxima retomar do cursor.
No reprocesso, a execução recebe os registros em ctx.input.quarantined ([{ id, record, reason }]) em vez de ler a origem; o que ela não mandar de volta para a quarentena sai como resolvido.
Contagens
Seção intitulada “Contagens”ctx.count(objeto, tipo, quantidade) alimenta o resumo da execução: read, created, updated, skipped e failed, por objeto (clientes, pedidos, produtos). Uma contagem failed deixa a execução partial.
Banco de dados local
Seção intitulada “Banco de dados local”- Uma consulta por chamada, só
SELECT(ouWITH ... SELECT), recusada no gateway e de novo no agente se mudar algo. - Paginação pela chave:
ORDER BY chaveeWHERE chave > :ultima, com a última chave emctx.state, emaxRowsaté o limite do gateway.truncated: truequer dizer que há mais linhas. - Marcadores de parâmetro de cada motor: SQL Server
@p1, Oracle:1, Firebird e MySQL?, Postgres$1. - Decimais chegam como texto exato e datas como texto ISO.
- No reconhecimento, a IA descobre tabelas e colunas pelas visões de catálogo de cada banco e olha os dados só em agregado.
Segredos
Seção intitulada “Segredos”Declare o nome em secrets no manifesto e use ctx.secret('NOME') onde o valor deve ir (um cabeçalho, um parâmetro, o soapHeader). A chamada devolve uma referência que o gateway troca pelo valor na saída: o valor nunca está no código, nos logs nem na conversa.
Erros que o código vê
Seção intitulada “Erros que o código vê”Uma recusa do gateway lança ConnectGatewayError com status e code (connect.route_not_allowed, connect.agent_offline…). Um limite da execução lança ConnectLimitError; mesmo capturado, a execução termina com falha por aquele motivo. Veja o catálogo de erros e os limites.