remoção do filtro 26 por filtro delta
This commit is contained in:
parent
98a531dbd3
commit
b0b76562b7
12 changed files with 348 additions and 542 deletions
162
src/filtroDelta.ts
Normal file
162
src/filtroDelta.ts
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
import z from "zod"
|
||||
|
||||
/**
|
||||
* Operadores permitidos para comparação.
|
||||
*/
|
||||
export const zOperadorFiltroDelta = z.enum([">", "<", ">=", "<=", "=", "!=", "in", "like"])
|
||||
|
||||
/**
|
||||
* Valida o identificador da condição.
|
||||
*
|
||||
* Formato esperado:
|
||||
* {identificador} ou {identificador:sufixo}
|
||||
*/
|
||||
const regexIdCondicaoFiltroDelta = /^\{[\w:]+\}$/
|
||||
|
||||
/**
|
||||
* Valida nomes de colunas e caminhos jsonb.
|
||||
*/
|
||||
const regexColunaFiltroDelta = /^\w+(\.\w+)*$/
|
||||
|
||||
/**
|
||||
* Coluna simples ou caminho aninhado de jsonb.
|
||||
*/
|
||||
export const zColunaFiltroDelta = z
|
||||
.string()
|
||||
.regex(regexColunaFiltroDelta, "Coluna inválida")
|
||||
.describe("nome da coluna ou caminho aninhado do jsonb")
|
||||
|
||||
/**
|
||||
* Estrutura individual de uma condição do filtro.
|
||||
*/
|
||||
export const zCondicaoFiltroDelta = z.object({
|
||||
/**
|
||||
* Identificador único da condição.
|
||||
*
|
||||
* Exemplo:
|
||||
* {1}
|
||||
*/
|
||||
id: z
|
||||
.string()
|
||||
.describe("id da condição do filtro")
|
||||
.regex(regexIdCondicaoFiltroDelta, "O id deve seguir o formato {identificador}"),
|
||||
|
||||
/**
|
||||
* Nome da coluna ou caminho jsonb.
|
||||
*/
|
||||
coluna: zColunaFiltroDelta,
|
||||
|
||||
/**
|
||||
* Operador da comparação.
|
||||
*/
|
||||
operador: zOperadorFiltroDelta,
|
||||
|
||||
/**
|
||||
* Valor utilizado na comparação.
|
||||
*/
|
||||
valor: z.any().describe("valor de comparação"),
|
||||
})
|
||||
|
||||
/**
|
||||
* Verifica se a fórmula possui uma estrutura lógica válida.
|
||||
*/
|
||||
const formulaFiltroDeltaValida = (formula: string) => {
|
||||
/**
|
||||
* Extrai apenas os tokens permitidos:
|
||||
* - referências de condição: {id} ou {id:sufixo}
|
||||
* - operadores lógicos: E, OU
|
||||
* - negação: NAO(...)
|
||||
* - parênteses
|
||||
*/
|
||||
const tokensPermitidos = formula.match(/\{[\w:]+\}|\bNAO\b|\bE\b|\bOU\b|\(|\)/g) || []
|
||||
|
||||
/**
|
||||
* Reconstrói a fórmula usando apenas os tokens válidos.
|
||||
*/
|
||||
const formulaReconstruida = tokensPermitidos.join("")
|
||||
|
||||
/**
|
||||
* Remove espaços para comparar o texto original com o texto reconstruído.
|
||||
*/
|
||||
const formulaOriginalSemEspacos = formula.replace(/\s+/g, "")
|
||||
const formulaReconstruidaSemEspacos = formulaReconstruida.replace(/\s+/g, "")
|
||||
|
||||
/**
|
||||
* Se forem diferentes, há conteúdo não permitido na fórmula.
|
||||
*/
|
||||
if (formulaOriginalSemEspacos !== formulaReconstruidaSemEspacos) {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Converte a fórmula para uma expressão JavaScript equivalente.
|
||||
*
|
||||
* NAO(x) → !x
|
||||
* E → &&
|
||||
* OU → ||
|
||||
*/
|
||||
const expressaoLogica = formula
|
||||
.replace(/\{[\w:]+\}/g, "true")
|
||||
.replace(/\bNAO\b/g, "!")
|
||||
.replace(/\bE\b/g, "&&")
|
||||
.replace(/\bOU\b/g, "||")
|
||||
|
||||
try {
|
||||
/**
|
||||
* Valida a ordem dos operadores, parênteses e estrutura lógica geral.
|
||||
*/
|
||||
new Function(`return (${expressaoLogica})`)()
|
||||
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Estrutura principal do filtro delta.
|
||||
*/
|
||||
export const zFiltroDelta = z
|
||||
.object({
|
||||
/**
|
||||
* Lista de condições disponíveis.
|
||||
*/
|
||||
condicoes: z.array(zCondicaoFiltroDelta),
|
||||
|
||||
/**
|
||||
* Fórmula lógica que combina as condições.
|
||||
*/
|
||||
formula: z.string().refine(formulaFiltroDeltaValida, {
|
||||
message:
|
||||
"Fórmula inválida. Use apenas referências de condições {id} ou {id:sufixo}, operadores E/OU/NAO e parênteses.",
|
||||
}),
|
||||
})
|
||||
.superRefine((value, ctx) => {
|
||||
/**
|
||||
* IDs das condições cadastradas.
|
||||
*/
|
||||
const idsCondicoes = new Set(value.condicoes.map((condicao) => condicao.id))
|
||||
|
||||
/**
|
||||
* Referências usadas dentro da fórmula.
|
||||
*/
|
||||
const referenciasFormula = value.formula.match(/\{[\w:]+\}/g) || []
|
||||
|
||||
/**
|
||||
* Verifica se toda referência da fórmula existe em condicoes.
|
||||
*/
|
||||
for (const referencia of referenciasFormula) {
|
||||
if (!idsCondicoes.has(referencia)) {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
path: ["formula"],
|
||||
message: `A referência ${referencia} não existe em condicoes.`,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export type tipoOperadorFiltroDelta = z.infer<typeof zOperadorFiltroDelta>
|
||||
export type tipoColunaFiltroDelta = z.infer<typeof zColunaFiltroDelta>
|
||||
export type tipoCondicaoFiltroDelta = z.infer<typeof zCondicaoFiltroDelta>
|
||||
export type tipoFiltroDelta = z.infer<typeof zFiltroDelta>
|
||||
Loading…
Add table
Add a link
Reference in a new issue