Hey @Scott_Collier-Weir and @Leandro_Zubrezki
I have a similar problem and have been at it for several days. I just cant for the life of me make it work. I did try to implement your suggestions but it just hast worked for me.
I have tried to just dump the data, map the data, push it. I did work, but I can find the version that forks from so many improvement attempts.
I just cant get my pack to write to the syctable. I´m likely on version 200 of this thing.
In the middle of trying to implement a way to loop to get the 5K lines of data and deal with the timeout I completely lost it.
Could you please be so kind to give me a hand here with this code.
I can retrieve the data to the log, just cont write to the table. Thank you very much.
import * as coda from "@codahq/packs-sdk";
/**
* This Coda Pack integrates with the Omie ERP system to retrieve client information.
* It provides a sync table ("OmieClients") that fetches client data from Omie using the Omie API.
* The pack uses App Key and App Secret authentication (no OAuth2).
* It automatically retrieves all client records across multiple pages, handling pagination.
*
* To use this pack, the user must:
* 1. Install the pack in their Coda document.
* 2. Provide their Omie App Key and App Secret when syncing the table.
* 3. Add the "OmieClients" sync table to their document.
* 4. Run the "SyncOmieClients" formula to load client data from Omie into the table.
*/
// --- Pack Definition ---
export const pack = coda.newPack(); // Creates a new Coda Pack instance
// --- Network Domain ---
pack.addNetworkDomain("omie.com.br"); // Specifies the allowed network domain for API calls, enhancing security
// --- API Endpoint ---
const OMIE_CLIENTS_API_ENDPOINT = "https://app.omie.com.br/api/v1/geral/clientes/"; // Defines the API endpoint for retrieving client data from Omie
// --- Fetch Clients Function ---
async function fetchOmieClients(context, appKey, appSecret, page = 1) {
let requestBody = {
call: "ListarClientes", // Specifies the API method to call: "ListarClientes" (List Clients)
app_key: appKey, // Uses the provided App Key
app_secret: appSecret, // Uses the provided App Secret
param: [
{
pagina: page, // Specifies the page number for pagination (defaults to 1)
registros_por_pagina: 100 // Specifies the number of records per page (100 in this case)
}
]
};
let response = await context.fetcher.fetch({
url: OMIE_CLIENTS_API_ENDPOINT,
method: "POST", // Specifies the HTTP method as POST
headers: {
"Content-Type": "application/json" // Sets the content type to JSON
},
body: JSON.stringify(requestBody) // Converts the request body to a JSON string
});
if (response.status !== 200) {
console.error("Omie API Error:", response); // Logs the error to the console
throw new Error(`Omie API Error: ${response.status} - ${response.body}`); // Throws an error with the status code and response body
}
let result = response.body;
if (result.fault) {
throw new Error(`Omie Error: ${result.fault.faultstring}`); // Throws an error with the error message from Omie
}
return result; // Returns the full response object (including the body)
}
// --- Client Schema ---
let ClientSchema = coda.makeObjectSchema({
properties: {
ID_ClienteOmie: { type: coda.ValueType.Number, description: "Omie's Client ID.", required: true, fromKey: "codigo_cliente_omie" },
Codigo_Cliente: { type: coda.ValueType.String, description: "Client Code.", fromKey: "codigo_cliente_integracao" },
Razao_Social: { type: coda.ValueType.String, description: "Client's Legal Name.", required: true, fromKey: "razao_social" },
Nome_Fantasia: { type: coda.ValueType.String, description: "Client's Trade Name.", fromKey: "nome_fantasia" },
CNPJ: { type: coda.ValueType.String, description: "Client's CNPJ.", fromKey: "cnpj_cpf" },
CPF: { type: coda.ValueType.String, description: "Client's CPF.", fromKey: "cnpj_cpf" },
Endereco: { type: coda.ValueType.String, description: "Client's Address.", fromKey: "endereco" },
Numero: { type: coda.ValueType.String, description: "Address Number.", fromKey: "endereco_numero" },
Complemento: { type: coda.ValueType.String, description: "Address Complement.", fromKey: "complemento" },
Bairro: { type: coda.ValueType.String, description: "Neighborhood.", fromKey: "bairro" },
CEP: { type: coda.ValueType.String, description: "Postal Code.", fromKey: "cep" },
Cidade: { type: coda.ValueType.String, description: "City.", fromKey: "cidade" },
Estado: { type: coda.ValueType.String, description: "State (e.g., SP).", fromKey: "estado" },
Telefone: { type: coda.ValueType.String, description: "Primary Phone Number.", fromKey: "telefone1_numero" },
Email: { type: coda.ValueType.String, description: "Email Address.", fromKey: "email" },
Inativo: { type: coda.ValueType.Boolean, description: "Is the client inactive?", fromKey: "inativo" },
Observacao: { type: coda.ValueType.String, description: "Additional notes.", fromKey: "observacao" },
//Tags: {
// type: coda.ValueType.Array,
// description: "Tags associated with the client.",
// items: { type: coda.ValueType.String, codaType: coda.ValueHintType.SelectList, options: ["Fornecedor", "Cliente", "Parceiro", "Outro"] },
// fromKey: "tags"
// },
Contato: { type: coda.ValueType.String, description: "Contact person.", fromKey: "contato" },
BloquearFaturamento: { type: coda.ValueType.Boolean, description: "Is invoicing blocked?", fromKey: "bloquear_faturamento" },
CidadeIbge: { type: coda.ValueType.String, description: "City IBGE Code.", fromKey: "cidade_ibge" },
DataCadastro: { type: coda.ValueType.String, description: "Date of registration.", fromKey: "data_cadastro" },
DataAlteracao: { type: coda.ValueType.String, description: "Date of last modification.", fromKey: "data_alteracao" },
Telefone2Numero: { type: coda.ValueType.String, description: "Secondary Phone Number.", fromKey: "telefone2_numero" },
Telefone2DDD: { type: coda.ValueType.String, description: "Secondary Phone Number DDD.", fromKey: "telefone2_ddd" },
Fax: { type: coda.ValueType.String, description: "Fax Number.", fromKey: "fax" },
Website: { type: coda.ValueType.String, description: "Website URL.", fromKey: "website" },
TipoPessoa: { type: coda.ValueType.String, description: "Type of Person (Física/Jurídica).", fromKey: "tipo_pessoa" },
InscricaoEstadual: { type: coda.ValueType.String, description: "State Registration.", fromKey: "inscricao_estadual" },
InscricaoMunicipal: { type: coda.ValueType.String, description: "Municipal Registration.", fromKey: "inscricao_municipal" }
},
displayProperty: "Razao_Social", // Specifies the property to display in Coda (Legal Name)
idProperty: "ID_ClienteOmie", // Specifies the property to use as the unique ID (Omie's Client ID)
identityName: "OmieClients" // Unique identity for this schema, needed for SyncTables
});
// --- Sync Table ---
pack.addSyncTable({
name: "OmieClients",
description: "Retrieves client data from Omie.",
schema: ClientSchema,
identityName: "OmieClients",
formula: {
name: "SyncOmieClients",
description: "Syncs clients from Omie. Provide App Key and App Secret.",
parameters: [
coda.makeParameter({
type: coda.ParameterType.String,
name: "appKey",
description: "Your Omie App Key."
}),
coda.makeParameter({
type: coda.ParameterType.String,
name: "appSecret",
description: "Your Omie App Secret."
})
],
async execute([appKey, appSecret], context) {
let allClients = [];
let page = 1;
let totalPages = 1;
// Fetch the first page to get the total number of pages
let firstResponse = await fetchOmieClients(context, appKey, appSecret, page);
if (!firstResponse || !firstResponse.body) {
return { result: [] }; // Return an empty array if no data is found
}
// Get the total number of pages from the first response
if (firstResponse.body.total_de_paginas) {
totalPages = firstResponse.body.total_de_paginas;
} else {
console.error("Unexpected response format:", firstResponse);
throw new Error("Unexpected response format: 'total_de_paginas' not found");
}
// Loop through all pages
for (let currentPage = 1; currentPage <= totalPages; currentPage++) {
let response = await fetchOmieClients(context, appKey, appSecret, currentPage);
if (!response || !response.body || !response.body.clientes_cadastro) {
continue; // Skip to the next page if there's an error or no data
}
// Push the mapped clients to the allClients array
allClients.push(...response.body.clientes_cadastro.map(client => ({
ID_ClienteOmie: client.codigo_cliente_omie,
Codigo_Cliente: client.codigo_cliente_integracao,
Razao_Social: client.razao_social,
Nome_Fantasia: client.nome_fantasia,
CNPJ: client.cnpj_cpf,
CPF: client.cnpj_cpf,
Endereco: client.endereco,
Numero: client.endereco_numero,
Complemento: client.complemento,
Bairro: client.bairro,
CEP: client.cep,
Cidade: client.cidade,
Estado: client.estado,
Telefone: client.telefone1_numero,
Email: client.email,
Inativo: client.inativo === "S",
Observacao: client.observacao,
Tags: client.tags ? client.tags.map(tag => tag.tag) : [],
Contato: client.contato,
BloquearFaturamento: client.bloquear_faturamento === "S",
CidadeIbge: client.cidade_ibge,
DataCadastro: client.data_cadastro,
DataAlteracao: client.data_alteracao,
Telefone2Numero: client.telefone2_numero,
Telefone2DDD: client.telefone2_ddd,
Fax: client.fax,
Website: client.website,
TipoPessoa: client.tipo_pessoa,
InscricaoEstadual: client.inscricao_estadual,
InscricaoMunicipal: client.inscricao_municipal
})));
}
return { result: allClients }; // Return the array of all client data
}
}
});
This is a jus dump version:
async execute([appKey, appSecret], context) {
let allClients = [];
let page = 1;
let totalPages = 1;
// Fetch the first page to get the total number of pages
let firstResponse = await fetchOmieClients(context, appKey, appSecret, page);
if (!firstResponse || !firstResponse.body) {
return { result: [] }; // Return an empty array if no data is found
}
// Get the total number of pages from the first response
if (firstResponse.body.total_de_paginas) {
totalPages = firstResponse.body.total_de_paginas;
} else {
console.error("Unexpected response format:", firstResponse);
throw new Error("Unexpected response format: 'total_de_paginas' not found");
}
// Loop through all pages
for (let currentPage = 1; currentPage <= totalPages; currentPage++) {
let response = await fetchOmieClients(context, appKey, appSecret, currentPage);
if (!response || !response.body || !response.body.clientes_cadastro) {
continue; // Skip to the next page if there's an error or no data
}
// Push the retrieved clients to the allClients array
//let allClientss[] = response.body.clientes_cadastro;
return { result: response.body.clientes_cadastro}; // Return the array of all client data
}
}
}
});
Thak so much once again…