Files
SLACK_COORD_RELATORIO/src/v2/sendGuard.js
T
Automacao GLPIandClaude Sonnet 5 64ddfb9b7b V2: guarda de envio real (SEND_ENABLED/FORCE_SEND), idempotencia por send_key e Slack nativo
- src/v2/sendGuard.js (novo): canSend() reduz SHADOW_MODE/DEV_MODE/
  SEND_ENABLED/FORCE_SEND a uma formula unica testada exaustivamente
  (secao 15 do ciclo final: matriz completa de guarda de seguranca).
  computeMessageHash/buildSendKey/isDuplicate implementam a chave
  logica de idempotencia da secao 13 (report_type+periodo+destino+hash).
- src/v2/config.js: SEND_ENABLED, FORCE_SEND, SLACK_DESTINATION.
- src/v2/pipeline.js: buildReport agora calcula envio.pode_enviar/
  motivo_guarda/duplicata_bloqueada/send_key a partir de triggerType
  (.mode) e sendLogRows (Data Table de log de envios).
- tools/build_v2_workflow*.js: novo Data Table 'Buscar Log de Envios',
  troca do HTTP Slack com webhook placeholder por node Slack nativo
  (credencial real lNqwBBrtLfkRqw2X, reaproveitada do projeto irmao
  Coordenador GLPI), branches de sucesso/falha gravando o log de envio
  so apos confirmacao real do Slack.
- test/sendGuard.test.js, test/workflowStructure.test.js (novos):
  cobrem a matriz de guarda de seguranca e travam regressao do bug de
  timeout (executeOnce=true nos 8 nodes de leitura em lote) e do bug
  de sendGuard.js nao concatenado no Code node gerado.
- Corrigido bug real encontrado nesta sessao: sendGuard.js nao estava
  na lista de modulos concatenados pelo gerador, deixando canSend/
  computeMessageHash/buildSendKey undefined em runtime.
- Deployado e validado ao vivo no DEV (workflow 3x5kb58TeBCvirm6):
  n8n_validate_workflow -> valid:true, 0 erros, executeOnce preservado
  nos 9 nodes de leitura em lote.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 09:22:42 -03:00

78 lines
2.9 KiB
JavaScript

'use strict';
/**
* Guarda de envio real (Slack) e idempotencia — secoes 12, 13 e 15 da
* missao original, usando o MESMO vocabulario (SEND_ENABLED/FORCE_SEND)
* ja em uso no projeto irmao "[DEV] Coordenador GLPI - Chamados Abertos
* v2 (Shadow)" (workflow y6JFEnp7Z2IJEUe2), para manter consistencia no
* ambiente em vez de inventar um padrao novo.
*
* Regra de decisao (todas as condicoes descritas na missao reduzidas a uma
* formula unica, testada exaustivamente em sendGuard.test.js):
*
* podeEnviar =
* SHADOW_MODE === false (absoluto — nada sobrepoe)
* && DEV_MODE === false (absoluto — nada sobrepoe; enquanto este
* workflow viver em DEV, NUNCA envia)
* && SEND_ENABLED === true (absoluto)
* && (isScheduled || FORCE_SEND === true) (manual so envia com override explicito)
*
* FORCE_SEND so tem efeito na execucao MANUAL (destrava o "manual nao
* envia por padrao"). Ele nunca sobrepoe SHADOW_MODE/DEV_MODE/SEND_ENABLED.
*/
function canSend({ triggerType, config }) {
const isScheduled = triggerType === 'scheduled';
if (config.SHADOW_MODE === true) {
return { podeEnviar: false, motivo: 'SHADOW_MODE=true (absoluto)' };
}
if (config.DEV_MODE === true) {
return { podeEnviar: false, motivo: 'DEV_MODE=true (absoluto, sem override enquanto viver em DEV)' };
}
if (config.SEND_ENABLED !== true) {
return { podeEnviar: false, motivo: 'SEND_ENABLED=false' };
}
if (!isScheduled && config.FORCE_SEND !== true) {
return { podeEnviar: false, motivo: 'execucao manual sem FORCE_SEND=true' };
}
return { podeEnviar: true, motivo: isScheduled ? 'scheduled + guards ok' : 'manual + FORCE_SEND=true' };
}
/**
* Hash estavel (nao criptografico — so precisa ser deterministico e
* compacto) do texto da mensagem, para a chave de idempotencia detectar
* se o CONTEUDO mudou entre duas tentativas do mesmo periodo.
*/
function computeMessageHash(texto) {
const str = String(texto || '');
let hash = 5381;
for (let i = 0; i < str.length; i += 1) {
hash = (hash * 33) ^ str.charCodeAt(i);
}
// >>> 0 converte para unsigned 32-bit antes de exibir em base36 (compacto)
return (hash >>> 0).toString(36);
}
/**
* Chave logica de idempotencia (secao 13): duas execucoes com a MESMA
* chave representam o MESMO relatorio logico e nao devem ser enviadas
* duas vezes, mesmo com retry/execucao manual.
*/
function buildSendKey({ reportType, periodStart, periodEnd, destination, messageHash }) {
return [reportType, periodStart, periodEnd, destination, messageHash].join('|');
}
/**
* Verifica se ja existe um envio com status=success para a mesma chave
* logica (report_type + period + destination + message_hash).
*/
function isDuplicate(sendLogRows, key) {
return (sendLogRows || []).some(
(row) => row && row.send_key === key && row.status === 'success'
);
}
module.exports = { canSend, computeMessageHash, buildSendKey, isDuplicate };