UNPKG

n8n-rdstation-crm

Version:

Nós personalizados do n8n para integração com a API do RD Station CRM (v1)

686 lines (638 loc) 17.3 kB
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription, NodeOperationError, } from 'n8n-workflow'; import { OptionsWithUri } from 'request-promise-native'; /** * Classe para o nó de Contatos do RD Station CRM * Este nó permite interações com a API de Contatos do RD Station CRM. */ export class RdStationCrmContacts implements INodeType { description: INodeTypeDescription = { // Informações básicas do nó displayName: 'RD Station CRM Contatos', name: 'rdStationCrmContacts', icon: 'file:rdstation.svg', // Ícone a ser exibido no n8n group: ['transform'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', description: 'Operações com contatos no RD Station CRM', defaults: { name: 'RD Station CRM Contatos', }, inputs: ['main'], outputs: ['main'], credentials: [ { name: 'rdStationCrmApi', required: true, }, ], properties: [ // Seleção do recurso (neste caso, só temos contatos) { displayName: 'Recurso', name: 'resource', type: 'options', noDataExpression: true, options: [ { name: 'Contato', value: 'contact', }, ], default: 'contact', required: true, }, // Seleção da operação a ser realizada { displayName: 'Operação', name: 'operation', type: 'options', noDataExpression: true, displayOptions: { show: { resource: [ 'contact', ], }, }, options: [ { name: 'Criar', value: 'create', description: 'Criar um novo contato', action: 'Criar um contato', }, { name: 'Atualizar', value: 'update', description: 'Atualizar um contato existente', action: 'Atualizar um contato', }, { name: 'Listar', value: 'getAll', description: 'Listar contatos', action: 'Listar contatos', }, { name: 'Obter', value: 'get', description: 'Obter um contato pelo UUID ou e-mail', action: 'Obter um contato', }, ], default: 'getAll', }, // Campos para a operação LISTAR CONTATOS { displayName: 'Parâmetros de Listagem', name: 'listParameters', type: 'collection', placeholder: 'Adicionar Parâmetros', default: {}, displayOptions: { show: { resource: [ 'contact', ], operation: [ 'getAll', ], }, }, options: [ { displayName: 'Limite', name: 'limit', type: 'number', default: 50, description: 'Número máximo de contatos a retornar', }, { displayName: 'Página', name: 'page', type: 'number', default: 1, description: 'Número da página para retornar', }, { displayName: 'Busca por Nome', name: 'q', type: 'string', default: '', description: 'Buscar contatos por nome', }, { displayName: 'Ordenação', name: 'order', type: 'options', options: [ { name: 'Nome', value: 'name', }, { name: 'Data de Criação', value: 'created_at', }, ], default: 'name', description: 'Campo pelo qual ordenar os resultados', }, { displayName: 'Direção da Ordenação', name: 'direction', type: 'options', options: [ { name: 'Ascendente', value: 'asc', }, { name: 'Descendente', value: 'desc', }, ], default: 'asc', description: 'Direção da ordenação (crescente ou decrescente)', }, { displayName: 'Filtrar por E-mail', name: 'email', type: 'string', default: '', description: 'Filtrar contatos por e-mail', }, { displayName: 'Filtrar por Telefone', name: 'phone', type: 'string', default: '', description: 'Filtrar contatos por telefone', }, { displayName: 'Filtrar por Cargo', name: 'title', type: 'string', default: '', description: 'Filtrar contatos por cargo', }, ], }, // Campos para a operação OBTER CONTATO { displayName: 'Tipo de Identificador', name: 'identifierType', type: 'options', options: [ { name: 'UUID', value: 'uuid', }, { name: 'E-mail', value: 'email', }, ], default: 'uuid', required: true, displayOptions: { show: { resource: [ 'contact', ], operation: [ 'get', ], }, }, description: 'Tipo de identificador para buscar o contato', }, { displayName: 'Valor do Identificador', name: 'identifierValue', type: 'string', default: '', required: true, displayOptions: { show: { resource: [ 'contact', ], operation: [ 'get', ], }, }, description: 'UUID ou e-mail do contato a ser obtido', }, // Campos para a operação ATUALIZAR CONTATO { displayName: 'Tipo de Identificador', name: 'updateIdentifierType', type: 'options', options: [ { name: 'UUID', value: 'uuid', }, { name: 'E-mail', value: 'email', }, ], default: 'uuid', required: true, displayOptions: { show: { resource: [ 'contact', ], operation: [ 'update', ], }, }, description: 'Tipo de identificador do contato a ser atualizado', }, { displayName: 'Valor do Identificador', name: 'updateIdentifierValue', type: 'string', default: '', required: true, displayOptions: { show: { resource: [ 'contact', ], operation: [ 'update', ], }, }, description: 'UUID ou e-mail do contato a ser atualizado', }, // Campos comuns para CRIAR e ATUALIZAR contatos { displayName: 'Dados do Contato', name: 'contactData', type: 'collection', placeholder: 'Adicionar Dados', default: {}, displayOptions: { show: { resource: [ 'contact', ], operation: [ 'create', 'update', ], }, }, options: [ { displayName: 'Nome', name: 'name', type: 'string', default: '', description: 'Nome do contato (obrigatório para criação, mínimo 2 caracteres)', }, { displayName: 'Cargo', name: 'title', type: 'string', default: '', description: 'Cargo ou título profissional do contato', }, { displayName: 'Data de Nascimento', name: 'birthday', type: 'string', default: '', description: 'Data de nascimento do contato (formato YYYY-MM-DD)', }, { displayName: 'ID da Empresa', name: 'organization_id', type: 'string', default: '', description: 'ID da empresa à qual o contato está vinculado', }, ], }, // E-mails do contato (para CRIAR e ATUALIZAR) { displayName: 'E-mails', name: 'emails', placeholder: 'Adicionar E-mail', type: 'fixedCollection', typeOptions: { multipleValues: true, }, displayOptions: { show: { resource: [ 'contact', ], operation: [ 'create', 'update', ], }, }, default: {}, options: [ { name: 'emailsValues', displayName: 'E-mail', values: [ { displayName: 'E-mail', name: 'email', type: 'string', default: '', description: 'Endereço de e-mail do contato', required: true, }, { displayName: 'Tipo', name: 'type', type: 'options', options: [ { name: 'Trabalho', value: 'work', }, { name: 'Pessoal', value: 'home', }, { name: 'Outro', value: 'other', }, ], default: 'work', description: 'Tipo de e-mail', }, ], }, ], }, // Telefones do contato (para CRIAR e ATUALIZAR) { displayName: 'Telefones', name: 'phones', placeholder: 'Adicionar Telefone', type: 'fixedCollection', typeOptions: { multipleValues: true, }, displayOptions: { show: { resource: [ 'contact', ], operation: [ 'create', 'update', ], }, }, default: {}, options: [ { name: 'phonesValues', displayName: 'Telefone', values: [ { displayName: 'Número', name: 'phone', type: 'string', default: '', description: 'Número de telefone do contato', required: true, }, { displayName: 'Tipo', name: 'type', type: 'options', options: [ { name: 'Móvel', value: 'mobile', }, { name: 'Trabalho', value: 'work', }, { name: 'Residencial', value: 'home', }, { name: 'Fax', value: 'fax', }, ], default: 'mobile', description: 'Tipo de telefone', }, ], }, ], }, ], }; /** * Método principal que executa o nó com base na operação selecionada */ async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> { const items = this.getInputData(); const returnData: INodeExecutionData[] = []; // URL base da API do RD Station CRM const baseUrl = 'https://crm.rdstation.com/api/v1'; const resource = this.getNodeParameter('resource', 0) as string; const operation = this.getNodeParameter('operation', 0) as string; // 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 const options: OptionsWithUri = { uri: '', // Uri inicial vazio, será definido conforme a operação headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, json: true, resolveWithFullResponse: true, }; // Vamos determinar qual operação executar com base no recurso e operação selecionados if (resource === 'contact') { if (operation === 'getAll') { // Operação: LISTAR CONTATOS // Preparando a requisição para listar contatos options.method = 'GET'; options.uri = `${baseUrl}/contacts`; // Obtendo e aplicando parâmetros de filtro/paginação const listParameters = this.getNodeParameter('listParameters', i) as { limit?: number; page?: number; q?: string; order?: string; direction?: string; email?: string; phone?: string; title?: string; }; const qs: Record<string, any> = {}; // Adicionando os parâmetros à query string if (listParameters.limit) qs.limit = listParameters.limit; if (listParameters.page) qs.page = listParameters.page; if (listParameters.q) qs.q = listParameters.q; if (listParameters.order) qs.order = listParameters.order; if (listParameters.direction) qs.direction = listParameters.direction; if (listParameters.email) qs.email = listParameters.email; if (listParameters.phone) qs.phone = listParameters.phone; if (listParameters.title) qs.title = listParameters.title; options.qs = qs; } else if (operation === 'get') { // Operação: OBTER CONTATO ESPECÍFICO // Determina o tipo de identificador (UUID ou email) const identifierType = this.getNodeParameter('identifierType', i) as string; const identifierValue = this.getNodeParameter('identifierValue', i) as string; // Verifica se o valor do identificador foi fornecido if (!identifierValue) { throw new NodeOperationError(this.getNode(), 'É necessário fornecer um UUID ou e-mail válido!', { itemIndex: i }); } // Configurando a requisição para buscar um contato específico options.method = 'GET'; options.uri = `${baseUrl}/contacts/${identifierValue}`; } else if (operation === 'create') { // Operação: CRIAR CONTATO // Configurando requisição para criar contato options.method = 'POST'; options.uri = `${baseUrl}/contacts`; // Obtendo os dados do contato const contactData = this.getNodeParameter('contactData', i) as { name?: string; title?: string; birthday?: string; organization_id?: string; }; // Verificando se o nome foi fornecido (obrigatório para criação) if (!contactData.name || contactData.name.length < 2) { throw new NodeOperationError( this.getNode(), 'O nome do contato é obrigatório e deve ter pelo menos 2 caracteres!', { itemIndex: i }, ); } // Preparando o corpo da requisição const body: any = { ...contactData }; // Adicionando e-mails ao corpo const emailsCollection = this.getNodeParameter('emails', i) as { emailsValues?: Array<{ email: string; type: string }>; }; if (emailsCollection.emailsValues && emailsCollection.emailsValues.length > 0) { body.emails = emailsCollection.emailsValues; } // 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; } else if (operation === 'update') { // Operação: ATUALIZAR CONTATO // Obtém o tipo e valor do identificador para atualização const identifierType = this.getNodeParameter('updateIdentifierType', i) as string; const identifierValue = this.getNodeParameter('updateIdentifierValue', i) as string; // Verifica se o valor do identificador foi fornecido if (!identifierValue) { throw new NodeOperationError( this.getNode(), 'É necessário fornecer um UUID ou e-mail válido para atualizar o contato!', { itemIndex: i }, ); } // Configurando a requisição para atualizar o contato options.method = 'PATCH'; options.uri = `${baseUrl}/contacts/${identifierValue}`; // Obtendo os dados a serem atualizados const contactData = this.getNodeParameter('contactData', i) as { name?: string; title?: string; birthday?: string; organization_id?: string; }; // Preparando o corpo da requisição const body: any = {}; // Adicionando apenas campos que não estão vazios if (contactData.name) body.name = contactData.name; if (contactData.title) body.title = contactData.title; if (contactData.birthday) body.birthday = contactData.birthday; if (contactData.organization_id) body.organization_id = contactData.organization_id; // Adicionando e-mails ao corpo, se fornecidos const emailsCollection = this.getNodeParameter('emails', i) as { emailsValues?: Array<{ email: string; type: string }>; }; if (emailsCollection.emailsValues && emailsCollection.emailsValues.length > 0) { body.emails = emailsCollection.emailsValues; } // 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 o contato!', { itemIndex: i }, ); } options.body = body; } } // Executa a requisição HTTP com as opções configuradas const response = await this.helpers.requestWithAuthentication.call( this, 'rdStationCrmApi', options, ); // Extrai os dados da resposta e adiciona ao retorno const responseData = response.body; returnData.push({ json: responseData, pairedItem: { item: i }, }); } catch (error) { // Trata e reporta erros ocorridos durante a execução if (this.continueOnFail()) { returnData.push({ json: { error: error instanceof Error ? error.message : String(error), }, pairedItem: { item: i }, }); continue; } throw error; } } return [returnData]; } }