| config | ||
| src | ||
| .env.development | ||
| .env.example | ||
| .gitignore | ||
| cron.js | ||
| ecosystem.config.js | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
Sistema de Sincronização ServiceNow <> GLPI
Visão Geral
Esta aplicação Node.js é um middleware projetado para realizar a sincronização bidirecional de tickets entre uma instância do ServiceNow (SNOW) e uma do GLPI. O objetivo é manter os tickets, seus status e comentários consistentes entre as duas plataformas, automatizando o fluxo de trabalho para as equipes de suporte.
A aplicação funciona como um job agendado (por exemplo, via cron) que executa um ciclo completo de sincronização.
Como Executar a Aplicação
Esta seção contém as instruções essenciais para configurar e rodar a aplicação em diferentes ambientes.
1. Pré-requisitos
- Node.js: Certifique-se de ter o Node.js (versão 18 ou superior) instalado.
- Dependências: Na raiz do projeto, execute o comando abaixo para instalar todas as dependências necessárias:
npm install - Arquivos de Ambiente: Crie os arquivos
.env.developmente.env.productionna raiz do projeto, baseando-se no exemplo.env.example(se houver) e preenchendo com as credenciais e URLs corretas para cada ambiente.
2. Executando com Scripts NPM
2. Configuração do Ambiente Python
A aplicação utiliza um script Python para sincronizar o mapeamento de localidades.
a) Dependências Python
Navegue até a pasta do script e instale as dependências listadas no requirements.txt:
cd src/scripts/python
pip install -r requirements.txt
b) Montagem do Compartilhamento de Arquivos (CIFS/Samba)
O script Python lê um arquivo .csv de um compartilhamento de rede. No servidor de produção, é necessário montar este compartilhamento para que o caminho definido em LOCATION_MAPPING_CSV_PATH seja acessível.
Adicione a seguinte linha ao seu arquivo /etc/fstab para montar o compartilhamento automaticamente durante o boot do sistema. Ajuste o IP, credenciais e caminhos conforme necessário.
//10.0.121.40/Tecnica/Controle\040CAOA/ENTIDADE\040SERVICE\040NOW /mnt/csv_share cifs username=<seu_usuario>,password=<sua_senha>,uid=1000,gid=1000,iocharset=utf8 0 0
Após editar o /etc/fstab, execute sudo mount -a para montar o compartilhamento imediatamente.
Os comandos abaixo utilizam a variável NODE_ENV para carregar o arquivo de ambiente (.env) correto.
Ambiente de Desenvolvimento
Para rodar em modo de desenvolvimento com reinício automático a cada alteração de arquivo (usando nodemon):
npm run dev
Ambiente de Produção
Para rodar a aplicação em modo de produção (usando node):
npm start
ou o comando explícito:
npm run start:prod
3. Executando com PM2
O PM2 é o gerenciador de processos recomendado para ambientes de produção. Ele garante que a aplicação reinicie automaticamente em caso de falhas e facilita o gerenciamento.
- Para iniciar em modo de produção:
pm2 start ecosystem.config.js --env production - Para iniciar em modo de desenvolvimento:
pm2 start ecosystem.config.js --env development - Comandos úteis do PM2:
pm2 list # Lista todos os processos pm2 logs sn-glpi-sync-cron # Exibe os logs em tempo real pm2 restart sn-glpi-sync-cron # Reinicia a aplicação pm2 stop sn-glpi-sync-cron # Para a aplicação
Fluxo de Execução
A aplicação segue uma ordem de execução estrita para garantir a consistência dos dados. O ponto de entrada é o arquivo src/app.js, que orquestra a chamada dos seguintes controladores:
-
processErrorController- Limpeza de Erros- Objetivo: Aumentar a resiliência do sistema.
- Ação: Busca por tickets que falharam em execuções anteriores (marcados com status de
error). Ele reseta o status desses tickets para um estado anterior válido (ex:pending_checkousynced), permitindo que eles sejam reprocessados no ciclo atual.
-
processTicketsController- Criação de Tickets- Objetivo: Trazer novos tickets do ServiceNow para o GLPI.
- Ação:
- Busca no SNOW: Invoca o
processSyncControllerpara buscar incidentes e requisições novas ou atualizadas no ServiceNow, utilizando uma "marca d'água" (timestamp) para otimização. - Criação no GLPI: Para cada novo ticket coletado, o
glpiTicketServiceé chamado para:- Verificar se o ticket já existe no GLPI para evitar duplicatas.
- Formatar os dados (título, descrição, categoria, etc.).
- Criar o ticket correspondente no banco de dados do GLPI.
- Busca no SNOW: Invoca o
-
processCommentsController- Sincronização de Comentários- Objetivo: Manter as conversas dos tickets sincronizadas.
- Ação: Para cada ticket ativo, realiza uma sincronização bidirecional de comentários (notas/follow-ups):
- SNOW -> GLPI: Busca novos comentários no ServiceNow, os salva no banco intermediário e os insere como follow-ups no GLPI.
- GLPI -> SNOW: Busca novos follow-ups no GLPI, os sanitiza (remove HTML, trata imagens) e os envia como
work_notespara o ServiceNow.
-
processStatusAndClosureController- Sincronização de Status e Fechamento- Objetivo: Manter os status dos tickets alinhados e gerenciar o ciclo de vida (resolução e fechamento).
- Ação: Realiza uma sincronização bidirecional de status, tratando reaberturas, pausas, resoluções e fechamentos em ambas as direções.
Regras de Negócio Principais
A aplicação implementa várias regras de negócio para gerenciar a complexidade da sincronização:
Controle de Fluxo com "Bastão" (source_last)
- Para evitar conflitos de atualização (race conditions), o sistema utiliza um campo
source_lastna tabelaticket_sync. - Este campo funciona como um "bastão da palavra": o sistema que realiza a última atualização (SNOW ou GLPI) "segura o bastão".
- O sistema oposto só pode atualizar o ticket se ele estiver com o bastão. Isso garante que as atualizações de status e comentários ocorram de forma ordenada.
Criação e Enriquecimento de Tickets
- Mapeamento: Tickets do ServiceNow são mapeados para o GLPI com base em regras de categoria, prioridade e SLA definidas no
ticketGlpiModel.js. - Título: O título do ticket no GLPI é padronizado como
[TIPO] - [ENTIDADE] - [TÍTULO ORIGINAL]. - Enriquecimento de Descrição: Se uma requisição do ServiceNow chega com a descrição vazia, o sistema busca automaticamente o conteúdo de variáveis de catálogo (como
justificativaetelefone) e as utiliza para preencher a descrição e os dados do ticket no GLPI.
Sincronização de Comentários
- Lógica de 4 Casos (GLPI -> SNOW): Para evitar duplicatas, a sincronização de comentários do GLPI para o SNOW verifica 4 cenários:
- Já Sincronizado: O comentário existe em ambos os locais. Nenhuma ação.
- Existe no SN, Falta no BD Local: O registro local é criado para corrigir o estado.
- Existe no BD Local, Falta no SN: O comentário é reenviado para o ServiceNow.
- Novo Comentário: O comentário é enviado para o SNOW e registrado localmente.
- Sanitização: Comentários do GLPI passam por um processo de limpeza (
commentSanitizer.js) que remove tags HTML e substitui imagens por um texto placeholder, garantindo que o conteúdo seja legível no ServiceNow.
Sincronização de Status
- Fechamento de Ticket em Espera: Se um ticket é resolvido no GLPI, mas seu correspondente no ServiceNow está com o status "Em Espera" ou "Aguardando Atendimento", o sistema primeiro altera o status no ServiceNow para "Em Atendimento" e só então prossegue com a resolução. Isso automatiza o fluxo e evita falhas.
- Regra "Fora do Escopo": Se um ticket no GLPI é solucionado com o tipo de solução "Fora do Escopo", o sistema não o resolve no ServiceNow. Em vez disso, ele adiciona uma nota de trabalho (
work_note) explicando o motivo e fecha o registro de sincronização, tratando-o como um caso especial. - Reabertura: O sistema permite a reabertura de tickets em ambas as direções, desde que o ticket não esteja permanentemente fechado.
Ambientes (.env)
- A aplicação suporta múltiplos ambientes através de arquivos
.env. - Se a variável de ambiente
NODE_ENVfor definida comoproduction, o arquivo.env.productionserá carregado. - Caso contrário (ou se
NODE_ENVfordevelopment), o arquivo.env.developmentserá utilizado.
Atualização do Mapeamento de Localidades (update_location.py)
- A aplicação conta com um script Python (
src/scripts/python/update_location.py) para manter o mapeamento entre as localidades do ServiceNow e as entidades do GLPI. - Fonte da Verdade: Um arquivo
.csvlocalizado em um servidor de arquivos (LOCATION_MAPPING_CSV_PATHno.env) é a fonte da verdade para este mapeamento. - Processo:
- Um usuário (
sothis) atualiza o arquivo.csvcom as correspondências corretas entre os nomes das localidades (SNOW) e os nomes das entidades (GLPI). - Um
cron jobno servidor executa o scriptupdate_location.pyperiodicamente. - O script lê o
.csv, busca os IDs correspondentes nos bancos de dados de ambos os sistemas e recria a tabelalocation_mappingno banco de dados intermediário (PostgreSQL).
- Um usuário (
- Resultado: Garante que os tickets criados no GLPI sejam sempre associados à entidade correta, com base em um mapeamento centralizado e de fácil manutenção.
Estrutura do Banco de Dados Intermediário (PostgreSQL)
tickets_sn: Armazena uma cópia local dos dados dos tickets do ServiceNow para otimizar consultas.ticket_sync: Tabela central que controla o estado da sincronização de cada ticket, incluindo os IDs de ambos os sistemas e o camposource_last.ticket_updates: Log de todas as atualizações (comentários, etc.), usado para evitar duplicidade e rastrear o histórico.location_mapping: Tabela de mapeamento entre as localizações do ServiceNow e as entidades do GLPI.sync_control: Armazena metadados do processo, como a marca d'água (timestamp) da última sincronização bem-sucedida.
Diagrama de Fluxo (Exemplo)
Sincronização de Resolução: GLPI -> ServiceNow
O diagrama abaixo ilustra como o sistema lida com um ticket que foi marcado como "Solucionado" no GLPI.
sequenceDiagram
participant App as app.js
participant StatusCtrl as processStatusAndClosureController
participant GlpiModel as TicketGlpiModel
participant SyncModel as TicketSyncModel
participant SnowService as servicenowService
participant ServiceNow as ServiceNow API
App->>StatusCtrl: Iniciar ciclo de status
StatusCtrl->>SyncModel: getTicketsToMonitor()
SyncModel-->>StatusCtrl: Lista de tickets ativos
loop Para cada ticket monitorado
StatusCtrl->>GlpiModel: getTicketStatus(glpi_id)
GlpiModel-->>StatusCtrl: Status 'Solucionado' (5)
alt Status mudou para 'Solucionado'
StatusCtrl->>GlpiModel: getTicketSolution(glpi_id)
GlpiModel-->>StatusCtrl: Detalhes da solução
StatusCtrl->>SnowService: closeTicketInServiceNow(sn_id, solucao)
SnowService->>ServiceNow: PATCH /api/now/table/incident/{sys_id} (state: 'Resolved')
ServiceNow-->>SnowService: 200 OK
SnowService-->>StatusCtrl: Sucesso
StatusCtrl->>SyncModel: updateStatus(sn_id, 'solved', 'solved')
SyncModel-->>StatusCtrl: OK
end
end