n8n-rdstation-crm
Version:
Nós personalizados do n8n para integração com a API do RD Station CRM (v1)
553 lines (508 loc) • 18.4 kB
text/typescript
import {
IExecuteFunctions,
INodeExecutionData,
INodeType,
INodeTypeDescription,
NodeOperationError,
} from 'n8n-workflow';
import { OptionsWithUri } from 'request-promise-native';
/**
* Classe para o nó de Empresas do RD Station CRM
* Este nó permite interações com a API de Empresas (Organizations) do RD Station CRM.
* As empresas são entidades jurídicas que podem agrupar contatos e negócios no CRM.
*/
export class RdStationCrmCompanies implements INodeType {
description: INodeTypeDescription = {
// Informações básicas do nó que serão exibidas na interface do n8n
displayName: 'RD Station CRM Empresas',
name: 'rdStationCrmCompanies',
icon: 'file:rdstation.svg', // Ícone a ser exibido no n8n
group: ['transform'], // Grupo onde o nó aparecerá na interface
version: 1, // Versão do nó
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', // Subtítulo dinâmico baseado nos parâmetros
description: 'Operações com empresas no RD Station CRM', // Descrição do nó
defaults: {
name: 'RD Station CRM Empresas', // Nome padrão quando o nó é adicionado
},
inputs: ['main'], // Define que o nó aceita uma entrada principal
outputs: ['main'], // Define que o nó tem uma saída principal
credentials: [
{
// Define que o nó requer credenciais do tipo rdStationCrmApi
name: 'rdStationCrmApi',
required: true, // As credenciais são obrigatórias
},
],
properties: [
// Seleção do recurso (neste caso, só temos empresas/organizations)
{
displayName: 'Recurso', // Label exibido na interface
name: 'resource', // Nome do parâmetro para referência interna
type: 'options', // Tipo do campo (seleção entre opções)
noDataExpression: true, // Não permite expressões dinâmicas
options: [
{
name: 'Empresa', // Nome amigável da opção
value: 'organization', // Valor interno da opção (organization é o termo usado na API)
},
],
default: 'organization', // Valor padrão selecionado
required: true, // Campo obrigatório
},
// Seleção da operação a ser realizada sobre as empresas
{
displayName: 'Operação', // Label do campo na interface
name: 'operation', // Nome do parâmetro internamente
type: 'options', // Campo de seleção entre opções
noDataExpression: true, // Não permite expressões dinâmicas
displayOptions: {
show: {
// Mostra este campo apenas quando o recurso for 'organization'
resource: [
'organization',
],
},
},
options: [
// Lista de operações disponíveis para empresas
{
name: 'Criar', // Operação de criação
value: 'create',
description: 'Criar uma nova empresa',
action: 'Criar uma empresa',
},
{
name: 'Atualizar', // Operação de atualização
value: 'update',
description: 'Atualizar uma empresa existente',
action: 'Atualizar uma empresa',
},
{
name: 'Listar', // Operação de listagem
value: 'getAll',
description: 'Listar empresas',
action: 'Listar empresas',
},
{
name: 'Obter', // Operação para obter detalhes
value: 'get',
description: 'Obter uma empresa pelo ID',
action: 'Obter uma empresa',
},
{
name: 'Listar Contatos da Empresa', // Operação para listar contatos associados
value: 'getContacts',
description: 'Listar contatos associados a uma empresa',
action: 'Listar contatos de uma empresa',
},
],
default: 'getAll', // Operação padrão
},
// Campos para a operação LISTAR EMPRESAS
{
displayName: 'Parâmetros de Listagem', // Título da seção
name: 'listParameters', // Nome para referência interna
type: 'collection', // Tipo coleção permite múltiplos parâmetros agrupados
placeholder: 'Adicionar Parâmetros', // Texto de placeholder
default: {}, // Valor padrão (vazio)
displayOptions: {
show: {
// Mostra apenas quando recurso é 'organization' e operação é 'getAll'
resource: [
'organization',
],
operation: [
'getAll',
],
},
},
options: [
// Opções de filtragem e paginação para listagem
{
displayName: 'Limite', // Controle de quantos itens retornar
name: 'limit',
type: 'number',
default: 50,
description: 'Número máximo de empresas a retornar',
},
{
displayName: 'Página', // Para navegação entre páginas
name: 'page',
type: 'number',
default: 1,
description: 'Número da página para retornar',
},
{
displayName: 'Busca por Nome', // Busca textual no nome
name: 'q',
type: 'string',
default: '',
description: 'Buscar empresas por nome',
},
{
displayName: 'ID do Segmento', // Filtrar por segmento de mercado
name: 'organization_segment_id',
type: 'string',
default: '',
description: 'Filtrar empresas por segmento específico',
},
{
displayName: 'ID do Usuário Responsável', // Filtrar por responsável
name: 'user_id',
type: 'string',
default: '',
description: 'Filtrar empresas por usuário responsável',
},
{
displayName: 'Ordenação', // Campo para ordenar resultados
name: 'order',
type: 'options',
options: [
{
name: 'Nome', // Ordem alfabética por nome
value: 'name',
},
{
name: 'Data de Criação', // Ordena por quando foi criada
value: 'created_at',
},
],
default: 'name',
description: 'Campo pelo qual ordenar os resultados',
},
{
displayName: 'Direção da Ordenação', // Crescente ou decrescente
name: 'direction',
type: 'options',
options: [
{
name: 'Ascendente', // A-Z ou mais antigo para mais recente
value: 'asc',
},
{
name: 'Descendente', // Z-A ou mais recente para mais antigo
value: 'desc',
},
],
default: 'asc',
description: 'Direção da ordenação (crescente ou decrescente)',
},
],
},
// Campos para a operação OBTER EMPRESA ou LISTAR CONTATOS DA EMPRESA
{
displayName: 'ID da Empresa', // Campo para informar ID da empresa
name: 'organizationId',
type: 'string', // Tipo texto para o ID
default: '',
required: true, // Campo obrigatório
displayOptions: {
show: {
// Mostrar apenas quando operação for 'get', 'update' ou 'getContacts'
resource: [
'organization',
],
operation: [
'get',
'update',
'getContacts',
],
},
},
description: 'ID da empresa a ser consultada/atualizada',
},
// Campos comuns para CRIAR e ATUALIZAR empresas
{
displayName: 'Dados da Empresa', // Seção para dados da empresa
name: 'organizationData',
type: 'collection', // Agrupamento de campos
placeholder: 'Adicionar Dados', // Texto de placeholder
default: {}, // Valor padrão (vazio)
displayOptions: {
show: {
resource: [
'organization',
],
operation: [
'create',
'update',
],
},
},
options: [
// Campos de dados da empresa
{
displayName: 'Nome', // Nome da empresa
name: 'name',
type: 'string',
default: '',
description: 'Nome da empresa (obrigatório para criação, mínimo 2 caracteres, deve ser único)',
},
{
displayName: 'ID do Segmento', // Segmento de mercado
name: 'segment_id',
type: 'string',
default: '',
description: 'ID do segmento da empresa',
},
{
displayName: 'Site', // Website da empresa
name: 'website',
type: 'string',
default: '',
description: 'URL do site da empresa',
},
{
displayName: 'E-mail', // E-mail geral da empresa
name: 'email',
type: 'string',
default: '',
description: 'E-mail de contato geral da empresa',
},
{
displayName: 'Endereço', // Endereço físico
name: 'address',
type: 'string',
default: '',
description: 'Endereço da empresa',
},
],
},
// Telefones da empresa (para CRIAR e ATUALIZAR)
{
displayName: 'Telefones', // Seção para números de telefone
name: 'phones',
placeholder: 'Adicionar Telefone', // Texto de placeholder
type: 'fixedCollection', // Coleção com estrutura fixa
typeOptions: {
multipleValues: true, // Permite múltiplos telefones
},
displayOptions: {
show: {
resource: [
'organization',
],
operation: [
'create',
'update',
],
},
},
default: {}, // Valor padrão (vazio)
options: [
{
name: 'phonesValues', // Nome interno da coleção
displayName: 'Telefone', // Título exibido na interface
values: [
{
displayName: 'Número', // Campo para número de telefone
name: 'phone',
type: 'string',
default: '',
description: 'Número de telefone da empresa',
required: true, // Número é obrigatório se um telefone for adicionado
},
{
displayName: 'Tipo', // Categorização do telefone
name: 'type',
type: 'options',
options: [
{
name: 'Trabalho', // Telefone comercial/principal
value: 'work',
},
{
name: 'Fax', // Número de fax
value: 'fax',
},
{
name: 'Outro', // Qualquer outro tipo
value: 'other',
},
],
default: 'work', // Padrão é telefone comercial
description: 'Tipo de telefone',
},
],
},
],
},
],
};
/**
* Método principal que executa o nó com base na operação selecionada
* Este método é chamado pelo n8n quando o fluxo é executado
*/
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData(); // Obtém os dados de entrada
const returnData: INodeExecutionData[] = []; // Array para armazenar resultados
// URL base da API do RD Station CRM
const baseUrl = 'https://crm.rdstation.com/api/v1';
const resource = this.getNodeParameter('resource', 0) as string; // Obtém o recurso selecionado
const operation = this.getNodeParameter('operation', 0) as string; // Obtém a operação selecionada
// Para cada item de entrada, processa a operação solicitada
for (let i = 0; i < items.length; i++) {
try {
// Configuração comum para todas as requisições HTTP
const options: OptionsWithUri = {
uri: '', // Uri inicial vazio, será definido conforme a operação
headers: {
'Accept': 'application/json', // Aceita respostas em JSON
'Content-Type': 'application/json', // Envia dados em formato JSON
},
json: true, // Processa automaticamente respostas JSON
resolveWithFullResponse: true, // Retorna o objeto de resposta completo
};
// Vamos determinar qual operação executar com base no recurso e operação selecionados
if (resource === 'organization') {
if (operation === 'getAll') {
// Operação: LISTAR EMPRESAS
// Preparando a requisição para listar empresas
options.method = 'GET'; // Método HTTP GET para listar
options.uri = `${baseUrl}/organizations`; // Endpoint de organizations (empresas)
// Obtendo e aplicando parâmetros de filtro/paginação definidos pelo usuário
const listParameters = this.getNodeParameter('listParameters', i) as {
limit?: number;
page?: number;
q?: string;
organization_segment_id?: string;
user_id?: string;
order?: string;
direction?: string;
};
const qs: Record<string, any> = {}; // Objeto para parâmetros de query string
// Adicionando os parâmetros à query string se estiverem definidos
if (listParameters.limit) qs.limit = listParameters.limit;
if (listParameters.page) qs.page = listParameters.page;
if (listParameters.q) qs.q = listParameters.q;
if (listParameters.organization_segment_id) qs.organization_segment_id = listParameters.organization_segment_id;
if (listParameters.user_id) qs.user_id = listParameters.user_id;
if (listParameters.order) qs.order = listParameters.order;
if (listParameters.direction) qs.direction = listParameters.direction;
options.qs = qs; // Anexa os parâmetros à requisição
} else if (operation === 'get') {
// Operação: OBTER EMPRESA ESPECÍFICA
// Obtém o ID da empresa a ser consultada
const organizationId = this.getNodeParameter('organizationId', i) as string;
// Verifica se o ID da empresa foi fornecido
if (!organizationId) {
throw new NodeOperationError(this.getNode(), 'É necessário fornecer um ID de empresa válido!', { itemIndex: i });
}
// Configurando a requisição para buscar uma empresa específica
options.method = 'GET';
options.uri = `${baseUrl}/organizations/${organizationId}`;
} else if (operation === 'getContacts') {
// Operação: LISTAR CONTATOS DA EMPRESA
// Obtém o ID da empresa para listar seus contatos
const organizationId = this.getNodeParameter('organizationId', i) as string;
// Verifica se o ID da empresa foi fornecido
if (!organizationId) {
throw new NodeOperationError(this.getNode(), 'É necessário fornecer um ID de empresa válido!', { itemIndex: i });
}
// Configurando a requisição para buscar contatos de uma empresa específica
options.method = 'GET';
options.uri = `${baseUrl}/organizations/${organizationId}/contacts`;
} else if (operation === 'create') {
// Operação: CRIAR EMPRESA
// Configurando requisição para criar empresa
options.method = 'POST'; // Método HTTP POST para criação
options.uri = `${baseUrl}/organizations`; // Endpoint de organizations
// Obtendo os dados da empresa
const organizationData = this.getNodeParameter('organizationData', i) as {
name?: string;
segment_id?: string;
website?: string;
email?: string;
address?: string;
};
// Verificando se o nome foi fornecido (obrigatório para criação)
if (!organizationData.name || organizationData.name.length < 2) {
throw new NodeOperationError(
this.getNode(),
'O nome da empresa é obrigatório e deve ter pelo menos 2 caracteres!',
{ itemIndex: i },
);
}
// Preparando o corpo da requisição
const body: any = { ...organizationData };
// Adicionando telefones ao corpo
const phonesCollection = this.getNodeParameter('phones', i) as {
phonesValues?: Array<{ phone: string; type: string }>;
};
if (phonesCollection.phonesValues && phonesCollection.phonesValues.length > 0) {
body.phones = phonesCollection.phonesValues;
}
options.body = body; // Anexa o corpo à requisição
} else if (operation === 'update') {
// Operação: ATUALIZAR EMPRESA
// Obtém o ID da empresa a ser atualizada
const organizationId = this.getNodeParameter('organizationId', i) as string;
// Verifica se o ID da empresa foi fornecido
if (!organizationId) {
throw new NodeOperationError(
this.getNode(),
'É necessário fornecer um ID de empresa válido para atualização!',
{ itemIndex: i },
);
}
// Configurando a requisição para atualizar a empresa
options.method = 'PUT'; // Método HTTP PUT para atualização
options.uri = `${baseUrl}/organizations/${organizationId}`; // Endpoint com ID da empresa
// Obtendo os dados a serem atualizados
const organizationData = this.getNodeParameter('organizationData', i) as {
name?: string;
segment_id?: string;
website?: string;
email?: string;
address?: string;
};
// Preparando o corpo da requisição
const body: any = { ...organizationData };
// Adicionando telefones ao corpo, se fornecidos
const phonesCollection = this.getNodeParameter('phones', i) as {
phonesValues?: Array<{ phone: string; type: string }>;
};
if (phonesCollection.phonesValues && phonesCollection.phonesValues.length > 0) {
body.phones = phonesCollection.phonesValues;
}
// Verifica se há campos para atualizar
if (Object.keys(body).length === 0) {
throw new NodeOperationError(
this.getNode(),
'É necessário fornecer pelo menos um campo para atualizar a empresa!',
{ itemIndex: i },
);
}
options.body = body; // Anexa o corpo à requisição
}
}
// Executa a requisição HTTP com as opções configuradas e autenticação
const response = await this.helpers.requestWithAuthentication.call(
this,
'rdStationCrmApi', // Usa as credenciais configuradas
options, // Opções da requisição
);
// Extrai os dados da resposta e adiciona ao retorno
const responseData = response.body;
returnData.push({
json: responseData, // Dados da resposta em formato JSON
pairedItem: { item: i }, // Mantém o pareamento com o item de entrada
});
} catch (error) {
// Trata e reporta erros ocorridos durante a execução
if (this.continueOnFail()) {
// Se configurado para continuar mesmo com falhas
returnData.push({
json: {
error: error instanceof Error ? error.message : String(error), // Inclui a mensagem de erro
},
pairedItem: { item: i },
});
continue; // Continua para o próximo item
}
throw error; // Senão, propaga o erro para interromper a execução
}
}
return [returnData]; // Retorna os dados processados
}
}