UniqueEmailValidator.java
package com.albertsilva.dev.asjcatalog.validation.user.validator;
import com.albertsilva.dev.asjcatalog.repository.UserRepository;
import com.albertsilva.dev.asjcatalog.validation.user.annotation.UniqueEmail;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
/**
* Validator de {@link UniqueEmail}: rejeita e-mail já cadastrado.
*
* <p>
* <b>Comportamento:</b> {@code null} ou em branco ⇒ <b>válido</b>. Caso
* contrário, normaliza o valor com {@code trim()} e {@code toLowerCase()} (sem
* {@code Locale}) e consulta {@code UserRepository.existsByEmailIgnoreCase}; se
* existir, registra {@code {user.email.unique}} e retorna {@code false}.
* </p>
*
* <p>
* <b>Limites conhecidos:</b> a consulta compara com o e-mail <em>gravado</em>
* ignorando a caixa, mas não remove espaços do valor gravado; e o mapper grava o
* e-mail como recebido. Portanto um e-mail persistido com espaços nas pontas
* não seria encontrado por esta verificação. A checagem e a gravação não são
* atômicas (a restrição {@code UNIQUE} do banco, sensível à caixa, é a garantia
* final). Não considera o usuário autenticado nem a URL; por isso é usado só
* em fluxos de criação.
* </p>
*
* <p>
* <b>Efeitos:</b> consulta o banco; sem acesso a HTTP, autenticação ou rede.
* </p>
*/
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
/**
* Repositório utilizado para consultar dados
* de usuários.
*/
private final UserRepository repository;
/**
* Construtor.
*
* @param repository repositório de usuários
*/
public UniqueEmailValidator(UserRepository repository) {
this.repository = repository;
}
/**
* Verifica se o e-mail (normalizado) ainda não existe na tabela de usuários.
*
* @param value e-mail informado (pode ser {@code null})
* @param context contexto usado para registrar a violação personalizada
* @return {@code true} se for nulo/em branco ou ainda não estiver cadastrado;
* {@code false} se já existir (mensagem {@code {user.email.unique}})
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null || value.isBlank()) {
return true;
}
String normalizedEmail = value.trim().toLowerCase();
boolean emailAlreadyExists = repository.existsByEmailIgnoreCase(normalizedEmail);
if (!emailAlreadyExists) {
return true;
}
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("{user.email.unique}").addConstraintViolation();
return false;
}
}