Как правильно задать propertyPath в ConstraintValidatorContext
Есть Constraint аннотация @ValidatePositions.
Валидируется объект Receipt, одно из полей которого List<Postition> positions.
Нужно провалидировать все позиции и в сообщение об ошибке пробросить только невалидные позиции. Подскажите, как это правильно сделать?
@Data
@NoArgsConstructor
@AllArgsConstructor
@ValidatePositions
class Receipt{
@Valid
@NotNull
@Size(min = 1)
List<Position> positions;
public boolean someFlag = true;
}
class Position {
public Integer id;
public String text;
public String ean;
public String plu;
...
}
//сама аннотация
@Documented
@Constraint(validatedBy = PositionsValidator.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidatePositions {
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
//реализация ConstraintValidator
public class PositionsValidator implements ConstraintValidator<ValidatePositions, Receipt> {
@Override
public boolean isValid(Receipt receipt, ConstraintValidatorContext context) {
var flag = receipt.someFlag;
var eanError = ValidationUtils.toOptionalError(
!flag || receipt.positions.stream().map(Position::getEan).noneMatch(StringUtils::isEmpty),
"Field 'receipt.positions.ean' must not be null"
);
//в таком виде в propertyPath попадает весь объект Receipt и сообщение становится слишком громоздким
context.buildConstraintViolationWithTemplate(eanError.get())
//пробовал так, но тоже громоздко. Весь массив не нужен
.addPropertyNode("positions")
//вроде можно еще как-то так, но не понимаю как указать индекс на конкретную позицию
.addPropertyNode("position").inIterable().atIndex(0)
//или так, но аналогично - как передать контейнер и индекс позиции
.inContainer(Position.class, 0).inIterable().atIndex(0)
.addConstraintViolation();
return eanError.isEmpty() ? true : false;
}
}