Pular para o conteúdo

SDK do conector (ctx)

Gerada dos tipos do pacote @zoppy-connect/connect-sdk (Zoppy-crm/zoppy-connect@f1ae3a72be71). Um conector é um arquivo TypeScript que exporta uma função e recebe o ctx:

import type { ConnectorContext, ConnectorResult } from '@zoppy-connect/connect-sdk';
export default async function run(ctx: ConnectorContext): Promise<ConnectorResult> {
let cursor = (ctx.state.get('cursor') as string | undefined) ?? '1970-01-01T00:00:00Z';
for (;;) {
const resposta = await ctx.http.request({
method: 'GET',
url: 'https://api.exemplo.com.br/v1/clientes',
headers: { Authorization: `Bearer ${ctx.secret('EXEMPLO_TOKEN')}` },
query: { alteradoDesde: cursor, limite: '100' },
});
const clientes = resposta.json() as { id: string; alteradoEm: string }[];
ctx.count('clientes', 'read', clientes.length);
if (clientes.length === 0) {
return { done: true };
}
cursor = clientes[clientes.length - 1].alteradoEm;
ctx.state.set('cursor', cursor);
if (ctx.shouldYield()) {
return { done: false };
}
}
}

O código não tem rede nem variáveis de ambiente: só o que está aqui. Imports só do Node ou de tipos; o resto é recusado na compilação. Os limites de cada fatia estão em Limites.

A função que o arquivo do conector exporta como default. Recebe o ctx e devolve nada, ou um ConnectorResult.

type ConnectorMain = (ctx: ConnectorContext) => Promise<ConnectorResult | undefined>;

O que a função do conector devolve. Sem retorno, ou com done: true, a execução termina.

Membro Tipo Descrição
done boolean (opcional) false pede mais uma fatia: guarde o cursor em ctx.state antes de devolver. Nada ou true termina a execução.
output Readonly<Record<string, JsonValue>> (opcional) O que a execução devolve a quem a chamou, na fatia que termina. Ignorado quando o manifesto não declara saída (o caso de todo conector na V1, que só roda pelo cron) e nas fatias que continuam.

O ctx: tudo o que o código do conector enxerga. O código roda sem rede; cada chamada abaixo passa pelo gateway, que só deixa sair o que o manifesto declara.

Membro Tipo Descrição
http { … } Chamadas HTTP pelo gateway, separadas entre ler e escrever.
http.request request(request: HttpRequest): Promise<HttpResponse> Lê do outro sistema, seja qual for o método: um POST que só consulta (comum em APIs que pesquisam por POST e em SOAP) também vai aqui. É de verdade também no ensaio. Um POST, PUT, PATCH ou DELETE feito aqui aparece na lista de escritas feitas como leitura (writesAsReads) do ensaio, para uma pessoa conferir.
http.write write(request: HttpRequest): Promise<HttpResponse> Cria, altera ou apaga algo no outro sistema. No ensaio, a chamada é registrada sem sair (planned: true). Em produção, exige externalWrites: true no manifesto, e a primeira escrita do conector em cada host espera uma pessoa confirmar: até lá ela fica guardada, as outras escritas daquela execução não saem e cada uma responde planned: true. Nada enviado aqui é desfeito.
soap soap(wsdlUrl: string, options?: SoapOptions): Promise<SoapClient> Um serviço SOAP pelo WSDL (buscado pelo gateway, num host do manifesto, com os imports). Cada operação vira uma função: client.read.<Operação>(args) quando só consulta, client.write.<Operação>(args) quando cria ou altera algo, com as mesmas regras de http.request e http.write. O WSDL é buscado uma vez por fatia.
xmlToJson xmlToJson(xml: string): JsonValue Converte XML em dados simples, do mesmo jeito que uma resposta SOAP chega: para um serviço que responde com XML dentro de um texto.
db LocalDatabases Consultas só de leitura num banco dentro da rede da loja (SQL Server, Oracle, Firebird, MySQL ou Postgres), pelo agente local que o manifesto indica em agents. O agente roda a consulta lá com o usuário só leitura dos segredos do conector; um único SELECT por chamada, e a lista local da máquina decide o que ele alcança.
secret secret(name: string): string Uma referência a um segredo do manifesto. O gateway troca a referência pelo valor na saída; o código nunca vê o valor.
state ConnectorState Memória que sobrevive entre fatias e entre execuções. Uma execução começa de onde a última que terminou bem (com sucesso ou parcial) parou.
log log(message: string, data?: JsonValue, level?: LogLevel): void Registra uma linha nos passos da execução, com dados opcionais. Dados pessoais são mascarados.
shouldYield shouldYield(): boolean Fica true quando a fatia está perto do fim: guarde o cursor em ctx.state e devolva { done: false } para continuar na próxima fatia.
mode RunMode live aplica as escritas; dry_run (o ensaio) as registra como planejadas. Leituras externas são de verdade nos dois.
quarantine quarantine(record: JsonValue, reason: string): void Separa um registro em vez de movê-lo, com o motivo, para uma pessoa revisar depois; a execução segue com o próximo e termina partial. Use para dado que o outro lado recusa (um 4xx) ou que não dá para mapear (CPF/CNPJ faltando, código de produto desconhecido). Há um teto de registros por fatia, e cada um conta para o limite de tamanho do log.
count count(object: string, kind: CountKind, amount?: number): void Conta o que aconteceu com os registros de um objeto nesta execução (pedidos, clientes, produtos): lidos na origem, criados ou atualizados do outro lado, ignorados (nada a mudar) ou com falha. O resumo da execução são essas contagens, por objeto e tipo.
input JsonValue | null O que iniciou a execução. No reprocesso da quarentena, { quarantined: [{ id, record, reason }] } com os registros escolhidos; numa execução do cron, manual ou de ensaio, null.

Guardado entre fatias e execuções.

Membro Tipo Descrição
get get(key: string): JsonValue | undefined Lê um valor guardado. Guarde aqui o cursor da origem (a data da última alteração lida, a última chave) e zere o que só uma execução usa (uma página).
set set(key: string, value: JsonValue): void Guarda um valor JSON para a próxima fatia e para a próxima execução.

live (produção) ou dry_run (ensaio).

type RunMode = 'live' | 'dry_run';

O tipo de contagem de ctx.count: lidos, criados, atualizados, ignorados ou com falha.

type CountKind = 'read' | 'created' | 'updated' | 'skipped' | 'failed';

Nível de uma linha de ctx.log: info (o padrão), warn ou error; warn e error ficam marcados para quem lê a execução.

type LogLevel = 'info' | 'warn' | 'error';

Uma chamada HTTP de ctx.http.request ou ctx.http.write.

Membro Tipo Descrição
method HttpMethod Método HTTP.
url string URL absoluta: https num host de hosts do manifesto, ou http(s)://host:porta de uma API de um agente do manifesto, que desce pelo túnel do agente até a rede da loja. O gateway recusa qualquer outra.
headers Readonly<Record<string, string>> (opcional) Cabeçalhos. Use ctx.secret('NOME') no valor de um cabeçalho de autenticação.
query Readonly<Record<string, string>> (opcional) Parâmetros da query string.
body unknown (opcional) Um texto vai como está; qualquer outro valor vai como JSON.

A resposta de uma chamada HTTP.

Membro Tipo Descrição
planned boolean true quando uma escrita não chegou ao outro sistema: no ensaio, ou numa escrita real esperando a primeira confirmação.
status number Código HTTP da resposta.
headers Readonly<Record<string, string>> Cabeçalhos da resposta.
body string Corpo da resposta como texto.
json json(): unknown O corpo interpretado como JSON.

Os métodos HTTP aceitos.

type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD';

Os bancos dos agentes do manifesto.

Membro Tipo Descrição
query query(query: SqlQuery): Promise<SqlResult> Roda uma consulta só de leitura num banco dos agentes do manifesto. É de verdade também no ensaio, como toda leitura.

Uma consulta de ctx.db.query.

Membro Tipo Descrição
database string O nome de um banco dos agentes do manifesto (agents[].databases[].name).
sql string Um SELECT (ou WITH ... SELECT), nada que mude dados. Os marcadores são os do próprio banco, por posição: SQL Server @p1, @p2...; Oracle :1, :2...; Firebird e MySQL ?; Postgres $1, $2.... Pagine na própria consulta: ORDER BY uma chave e WHERE chave > última lida, com a última guardada em ctx.state.
params readonly SqlParam[] (opcional) Os parâmetros, na ordem dos marcadores.
maxRows number (opcional) No máximo esta quantidade de linhas (o gateway limita); se houver mais, a resposta vem com truncated: true.

Um parâmetro da consulta. Um Date vai como data e hora; para decimal exato, data sem hora ou bytes, diga o tipo: { type: 'decimal', value: '1234.50' }, { type: 'date', value: '2026-10-31' }, { type: 'binary', value: base64 }.

type SqlParam =
| string
| number
| boolean
| null
| Date
| { readonly type: 'datetime' | 'date' | 'decimal' | 'binary'; readonly value: string };

O resultado de uma consulta.

Membro Tipo Descrição
columns readonly SqlColumn[] As colunas, na ordem do SELECT, com o tipo de cada uma.
rows readonly SqlRow[] As linhas, cada uma por nome de coluna.
rowCount number Quantas linhas vieram.
truncated boolean true quando a consulta tinha mais linhas que maxRows (ou que o tamanho de uma resposta): leia a próxima página.

Uma coluna do resultado.

Membro Tipo Descrição
name string Nome (ou apelido) da coluna.
type SqlColumnType Como os valores da coluna chegam.

Decimais e inteiros grandes chegam como texto exato, datas e horas como texto ISO, bytes em base64.

type SqlColumnType =
| 'text'
| 'integer'
| 'decimal'
| 'float'
| 'boolean'
| 'date'
| 'time'
| 'datetime'
| 'datetimeoffset'
| 'binary'
| 'other';

Uma linha por nome de coluna. Dê um apelido único a cada coluna: nome repetido fica com o último valor.

type SqlRow = { readonly [column: string]: JsonValue };

Opções de ctx.soap.

Membro Tipo Descrição
endpoint string (opcional) O endereço a chamar no lugar do soap:address do WSDL: https, num host do manifesto.
port string (opcional) A porta do WSDL, pelo nome; por padrão, a primeira porta SOAP 1.1.
headers Readonly<Record<string, string>> (opcional) Cabeçalhos HTTP de toda chamada, inclusive a busca do WSDL (um Authorization com ctx.secret).
soapHeader string (opcional) XML que vai dentro do soap:Header de toda chamada (WS-Security, um token de sessão), com segredos como ctx.secret('NOME').

O cliente de um serviço SOAP.

Membro Tipo Descrição
endpoint string O endereço que o cliente chama.
operations readonly string[] As operações da porta, por nome.
read { … } Operações que só consultam: de verdade também no ensaio.
read.[operation: string] SoapOperation Uma operação de leitura, pelo nome.
write { … } Operações que criam, alteram ou apagam algo no outro sistema: escrita externa, planejada no ensaio.
write.[operation: string] SoapOperation Uma operação de escrita, pelo nome.

Uma operação do serviço: recebe os argumentos e devolve a resposta.

type SoapOperation = (args?: SoapArguments) => Promise<SoapResponse>;

Os argumentos de uma operação: os campos de entrada por nome, em qualquer ordem (o cliente escreve na ordem do WSDL).

Membro Tipo Descrição
[field: string] JsonValue Um campo de entrada.

A resposta de uma operação SOAP.

Membro Tipo Descrição
planned boolean true quando uma escrita não chegou ao outro sistema (ensaio, ou esperando a primeira confirmação): data vem null.
status number Código HTTP da resposta.
fault SoapFault | null O soap:Fault que o serviço respondeu, ou null.
data JsonValue O conteúdo do elemento de saída da operação como dados simples, por nome de elemento, sem namespaces: todo valor é texto (converta números e datas), xsi:nil vira null, e um campo que o WSDL diz que repete é sempre uma lista.
xml string A resposta como chegou, para o que data deixa de fora (atributos).

Um soap:Fault.

Membro Tipo Descrição
code string Código da falha.
message string Mensagem da falha.
detail JsonValue Detalhe da falha, como dados simples.

Qualquer valor JSON.

type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };

O gateway recusou ou não completou a chamada: host fora do manifesto, segredo desconhecido, endereço bloqueado, conector pausado. O code é um dos códigos connect.* do catálogo de erros.

Membro Tipo Descrição
status number Código HTTP da recusa.
code string Código estável do erro (ex.: connect.route_not_allowed).

Um limite da execução foi atingido. O contexto lembra do erro: a execução termina com falha por esse motivo mesmo que o código capture a exceção.

Membro Tipo Descrição
reason LimitReason Qual limite foi atingido.

Os limites que geram ConnectLimitError: chamadas, tamanho de resposta, tamanho do log, registros em quarentena.

type LimitReason = 'calls' | 'response_size' | 'log_size' | 'quarantine';