diff --git a/clearing-parent/clearing-utils/pom.xml b/clearing-parent/clearing-utils/pom.xml index e0e9bbd67..59e076a6b 100644 --- a/clearing-parent/clearing-utils/pom.xml +++ b/clearing-parent/clearing-utils/pom.xml @@ -86,6 +86,10 @@ assertj-core test + + ru.spcex.platform + platform-messaging + diff --git a/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/context/IValidatorContext.java b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/context/IValidatorContext.java new file mode 100644 index 000000000..d1003c026 --- /dev/null +++ b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/context/IValidatorContext.java @@ -0,0 +1,7 @@ +package ru.spcex.clearing.validation.common.context; + +public interface IValidatorContext { + default S getStoredObject(Enum storedObject) { + throw new UnsupportedOperationException("not implemented"); + } +} diff --git a/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/context/ImdgValidationContext.java b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/context/ImdgValidationContext.java new file mode 100644 index 000000000..6e205e4eb --- /dev/null +++ b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/context/ImdgValidationContext.java @@ -0,0 +1,54 @@ +package ru.spcex.clearing.validation.common.context; + +import ru.spcex.platform.classes.base.SpcexObjectBase; +import ru.spcex.platform.imdg.api.Imdg; + +import java.util.HashMap; +import java.util.Map; + + +public class ImdgValidationContext implements IValidatorContext { + private final Map> imdgs; + private final Map, Object> storedObjects; + private T validatedObject; + + public ImdgValidationContext() { + this.imdgs = new HashMap<>(); + this.storedObjects = new HashMap<>(); + } + + public void addImdg(String key, Imdg imdg) { + imdgs.put(key, imdg); + } + + public void setValidatedObject(T validatedObject) { + this.validatedObject = validatedObject; + } + + public T getValidatedObject() { + return validatedObject; + } + + /** + * !!!не использовать один Enum в разных местах + */ + public void storeObject(Enum storedObject, Object anyObject) { + storedObjects.put(storedObject, anyObject); + } + + @Override + @SuppressWarnings("unchecked") + public S getStoredObject(Enum storedObject) { + Object o = storedObjects.get(storedObject); + return (S) o; + } + + @SuppressWarnings("unchecked") + public Imdg obtainMap(String mapName, Class clazz) { + Imdg imdg = imdgs.get(mapName); + if (imdg == null) { + throw new IllegalStateException(mapName + " imdg wasn't added to validation context"); + } + return (Imdg) imdg; + } +} diff --git a/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/DateNotBeforeRule.java b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/DateNotBeforeRule.java new file mode 100644 index 000000000..154275358 --- /dev/null +++ b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/DateNotBeforeRule.java @@ -0,0 +1,61 @@ +package ru.spcex.clearing.validation.common.rules; + +import ru.spcex.clearing.validation.common.context.ImdgValidationContext; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.enumeration.IEnumId; + +import java.time.LocalDate; +import java.util.Optional; +import java.util.function.Function; + +/** + * Проверка поля с LocalDate. Условие проверки: поверяемая дата >= текущая дата + * @param Класс проверяемого объекта + */ +public record DateNotBeforeRule( + String fieldName, + Function getter, + IEnumId errorEmptyRequiredValue, + IEnumId errorTaskForPastDate, + boolean required +) implements IValidationRule> { + + /** + * @param fieldName Название поля класса, используется для передачи ошибки + * @param getter Метод получения проверяемой даты + * @param required Флаг обязательности поля + * @param errorEmptyRequiredValue Ошибка, которая будет возвращена, если поле пустое + * @param errorTaskForPastDate Ошибка, которая будет возвращена, если проверяемая дата меньше текущей + * @param Класс проверяемого объекта + */ + public static DateNotBeforeRule instance(String fieldName, + Function getter, + IEnumId errorEmptyRequiredValue, + IEnumId errorTaskForPastDate, + boolean required) { + return new DateNotBeforeRule<>(fieldName, getter, errorEmptyRequiredValue, errorTaskForPastDate, required); + } + + /** + * @param fieldName Название поля класса, используется для передачи ошибки + * @param getter Метод получения проверяемой даты + * @param errorEmptyRequiredValue Ошибка, которая будет возвращена, если поле пустое + * @param errorTaskForPastDate Ошибка, которая будет возвращена, если проверяемая дата меньше текущей + * @param Класс проверяемого объекта + */ + public static DateNotBeforeRule instance(String fieldName, + Function getter, + IEnumId errorEmptyRequiredValue, + IEnumId errorTaskForPastDate) { + return new DateNotBeforeRule<>(fieldName, getter, errorEmptyRequiredValue, errorTaskForPastDate, true); + } + + @Override + public Optional validate(ImdgValidationContext context) { + R validatedObject = context.getValidatedObject(); + LocalDate date = getter.apply(validatedObject); + if (date == null) return required ? of(errorEmptyRequiredValue, fieldName) : Optional.empty(); + if (date.isBefore(LocalDate.now())) return of(errorTaskForPastDate, fieldName); + return Optional.empty(); + } +} diff --git a/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/DictionaryPresentRule.java b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/DictionaryPresentRule.java new file mode 100644 index 000000000..11d2bc0c0 --- /dev/null +++ b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/DictionaryPresentRule.java @@ -0,0 +1,102 @@ +package ru.spcex.clearing.validation.common.rules; + +import ru.clearing.platform.dictionary.AbstractDictionary; +import ru.spcex.clearing.validation.common.context.ImdgValidationContext; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.predicate.ImdgPredicate; +import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.enumeration.IEnumId; + +import java.util.Collection; +import java.util.Optional; +import java.util.function.Function; + +/** + * Проверка поля с кодом из словаря (таблицы *_dictionary) + * @param Класс проверяемого объекта + * @param Класс словаря + */ +public record DictionaryPresentRule(String fieldName, + Function getter, + String dictionaryName, + Class dictionaryClass, + IEnumId errorEmptyRequiredValue, + IEnumId errorWrongDictionaryValue, + boolean required) implements IValidationRule> { + + /** + * @param fieldName Название поля + * @param getter Метод получения проверяемого значения + * @param dictionaryName Название словаря (см. IMDGDistributedNames) + * @param dictionaryClass Класс словаря (классы *Dictionary.class) + * @param errorEmptyRequiredValue Ошибка, которая будет возвращена, если поле пустое + * @param errorWrongDictionaryValue Ошибка, которая будет возвращена, если поле не найдено в словаре + * @param required Флаг обязательности + * @param Класс проверяемого объекта + * @param Класс словаря + */ + public static DictionaryPresentRule instance(String fieldName, + Function getter, + String dictionaryName, + Class dictionaryClass, + IEnumId errorEmptyRequiredValue, + IEnumId errorWrongDictionaryValue, + boolean required) { + return new DictionaryPresentRule<>(fieldName, + getter, + dictionaryName, + dictionaryClass, + errorWrongDictionaryValue, + errorEmptyRequiredValue, + required); + } + + /** + * @param fieldName Название поля + * @param getter Метод получения проверяемого значения + * @param dictionaryName Название словаря (см. IMDGDistributedNames) + * @param dictionaryClass Класс словаря (классы *Dictionary.class) + * @param errorEmptyRequiredValue Ошибка, которая будет возвращена, если поле пустое + * @param errorWrongDictionaryValue Ошибка, которая будет возвращена, если поле не найдено в словаре + * @param Класс проверяемого объекта + * @param Класс словаря + */ + public static DictionaryPresentRule instance(String fieldName, + Function getter, + String dictionaryName, + Class dictionaryClass, + IEnumId errorEmptyRequiredValue, + IEnumId errorWrongDictionaryValue) { + return new DictionaryPresentRule<>(fieldName, + getter, + dictionaryName, + dictionaryClass, + errorEmptyRequiredValue, + errorWrongDictionaryValue, + true); + } + + @Override + public Optional validate(ImdgValidationContext context) { + R validatedObject = context.getValidatedObject(); + String enumCode = getter.apply(validatedObject); + if (enumCode == null) return required ? of(errorEmptyRequiredValue, fieldName) : empty(); + Imdg dictImdg = context.obtainMap( + dictionaryName, + dictionaryClass + ); + ImdgPredicateBuilder imdgPredicateBuilder = dictImdg.predicateBuilder(); + ImdgPredicate imdgPredicate = imdgPredicateBuilder.equals("code", enumCode); + Collection fromMap = dictImdg.getCollectionObjectsByPredicate(imdgPredicate); + if (fromMap.isEmpty()) { + return of(errorWrongDictionaryValue, fieldName); + } + assert fromMap.size() == 1; + D dictionaryFromMap = fromMap.iterator().next(); + if (dictionaryFromMap == null) { + return of(errorWrongDictionaryValue, fieldName); + } + return empty(); + } +} diff --git a/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/EnumPresentRule.java b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/EnumPresentRule.java similarity index 84% rename from clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/EnumPresentRule.java rename to clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/EnumPresentRule.java index ae05b8f70..f32366483 100644 --- a/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/EnumPresentRule.java +++ b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/EnumPresentRule.java @@ -1,10 +1,9 @@ -package ru.spcex.clearing.validation.common; +package ru.spcex.clearing.validation.common.rules; -import ru.spcex.platform.imdg.validation.ImdgValidationContext; +import ru.spcex.clearing.validation.common.context.ImdgValidationContext; import ru.spcex.platform.utils.enumeration.EnumMessage; import ru.spcex.platform.utils.enumeration.IEnumId; import ru.spcex.platform.utils.enumeration.IEnumKey; -import ru.spcex.platform.utils.validation.IValidationRule; import java.util.Optional; import java.util.function.Function; @@ -25,6 +24,8 @@ public record EnumPresentRule(String fieldName, * @param fieldName Название поля * @param getter Метод получения проверяемого значения * @param enumValues Значения из проверяемого множества (Enum.values()) + * @param errorWrongEnumValue Ошибка, которая будет возвращена, если поле не найдено в Enum + * @param errorEmptyRequiredValue Ошибка, которая будет возвращена, если поле пустое * @param required Флаг обязательности * @param Класс проверяемого объекта * @param Enum @@ -44,6 +45,8 @@ public record EnumPresentRule(String fieldName, * @param fieldName Название поля * @param getter Метод получения проверяемого значения * @param enumValues Значения из проверяемого множества (Enum.values()) + * @param errorWrongEnumValue Ошибка, которая будет возвращена, если поле не найдено в Enum + * @param errorEmptyRequiredValue Ошибка, которая будет возвращена, если поле пустое * @param Класс проверяемого объекта * @param Enum */ diff --git a/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/FieldRequiredRule.java b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/FieldRequiredRule.java similarity index 69% rename from clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/FieldRequiredRule.java rename to clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/FieldRequiredRule.java index 358df8caa..61cfd4913 100644 --- a/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/FieldRequiredRule.java +++ b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/FieldRequiredRule.java @@ -1,24 +1,29 @@ -package ru.spcex.clearing.validation.common; +package ru.spcex.clearing.validation.common.rules; -import ru.spcex.platform.imdg.validation.ImdgValidationContext; +import ru.spcex.clearing.validation.common.context.ImdgValidationContext; import ru.spcex.platform.utils.enumeration.EnumMessage; import ru.spcex.platform.utils.enumeration.IEnumId; -import ru.spcex.platform.utils.validation.IValidationRule; import java.util.Optional; import java.util.function.Function; /** * Проверка обязательности поля (not null) + * @param errorEmptyRequiredValue Ошибка, которая будет возвращена, если поле пустое * @param Класс проверяемого объекта * @param Класс проверяемого значения */ -public record FieldRequiredRule(String fieldName, Function getter, IEnumId errorEmptyRequiredValue) implements IValidationRule> { +public record FieldRequiredRule( + String fieldName, + Function getter, + IEnumId errorEmptyRequiredValue +) implements IValidationRule> { /** * @param fieldName Название поля * @param getter Метод получения значения + * @param errorEmptyRequiredValue Ошибка, которая будет возвращена, если поле пустое * @param Класс проверяемого объекта * @param Класс проверяемого значения */ diff --git a/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/IValidationRule.java b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/IValidationRule.java new file mode 100644 index 000000000..93720b21b --- /dev/null +++ b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/IValidationRule.java @@ -0,0 +1,44 @@ +package ru.spcex.clearing.validation.common.rules; + +import ru.spcex.clearing.validation.common.context.IValidatorContext; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.enumeration.IEnumId; + +import java.util.Optional; + +/** + * Правило валидации, описывающее какую ошибку возвращать и при каких условиях + * Все необходимые данные берет из контекста + * @param контекст + */ +public interface IValidationRule { + Optional validate(C context); + + /** + * для логов + */ + default String ruleName() { + return getClass().getSimpleName(); + } + + /** + * если у валидатора был вызван метод проверить все, этот метод может использоваться для более тонкой настройки + * по дефолту - выполнять проверку даже если предыдущие выполнены с ошибками + */ + default boolean runIfErrorsPresent() { + return true; + } + + //*************** методы для сокращения кода *************** + default Optional of(IEnumId error) { + return Optional.of(new EnumMessage(error)); + } + + default Optional of(IEnumId error, Object... args) { + return Optional.of(new EnumMessage(error, args)); + } + + default Optional empty() { + return Optional.empty(); + } +} diff --git a/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/IdPresentRule.java b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/IdPresentRule.java new file mode 100644 index 000000000..c5d118461 --- /dev/null +++ b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/IdPresentRule.java @@ -0,0 +1,105 @@ +package ru.spcex.clearing.validation.common.rules; + +import ru.spcex.clearing.validation.common.context.ImdgValidationContext; +import ru.spcex.platform.classes.base.SpcexObjectBase; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.enumeration.IEnumId; + +import java.util.Optional; +import java.util.function.Function; + +/** + * Проверка наличия связанного по Id объекта в базе + * @param Класс проверяемого объекта + * @param Класс связанного объекта + */ +public record IdPresentRule( + String fieldName, + Function idGetter, + String mapName, + Class mapClass, + IEnumId errorEmptyRequiredValue, + IEnumId errorIdNotPresent, + boolean required, + Function[] additionalChecks +) implements IValidationRule> { + + /** + * @param fieldName Название поля, содержащего Id + * @param idGetter Метод получения Id + * @param mapName Название мапы связанного объекта (см. IMDGDistributedNames) + * @param mapClass Класс связанного объекта + * @param errorEmptyRequiredValue Ошибка, которая будет возвращена, если поле пустое + * @param errorIdNotPresent Ошибка, которая будет возвращена, если Id не найден + * @param required Флаг обязательности + * @param additionalChecks Дополнительные проверки, которые можно провести со связанным объектом + * @param Класс проверяемого объекта + * @param Класс связанного объекта + */ + @SafeVarargs + public static IdPresentRule instance(String fieldName, + Function idGetter, + String mapName, + Class mapClass, + IEnumId errorEmptyRequiredValue, + IEnumId errorIdNotPresent, + boolean required, + Function ... additionalChecks) { + return new IdPresentRule<>(fieldName, + idGetter, + mapName, + mapClass, + errorEmptyRequiredValue, + errorIdNotPresent, + required, + additionalChecks); + } + + /** + * @param fieldName Название поля, содержащего Id + * @param idGetter Метод получения Id + * @param mapName Название мапы связанного объекта (см. IMDGDistributedNames) + * @param mapClass Класс связанного объекта + * @param errorEmptyRequiredValue Ошибка, которая будет возвращена, если поле пустое + * @param errorIdNotPresent Ошибка, которая будет возвращена, если Id не найден + * @param additionalChecks Дополнительные проверки, которые можно провести со связанным объектом + * @param Класс проверяемого объекта + * @param Класс связанного объекта + */ + @SafeVarargs + public static IdPresentRule instance(String fieldName, + Function idGetter, + String mapName, + Class mapClass, + IEnumId errorEmptyRequiredValue, + IEnumId errorIdNotPresent, + Function ... additionalChecks) { + return new IdPresentRule<>(fieldName, + idGetter, + mapName, + mapClass, + errorEmptyRequiredValue, + errorIdNotPresent, + true, + additionalChecks); + } + + @Override + public Optional validate(ImdgValidationContext context) { + R validatedObject = context.getValidatedObject(); + Long id = idGetter.apply(validatedObject); + if (id == null) return required ? of(errorEmptyRequiredValue, fieldName) : empty(); + Imdg imdg = context.obtainMap(mapName, mapClass); + V fromMap = imdg.getSingleObjectByID(id); + if (fromMap == null) { + return of(errorIdNotPresent, fieldName); + } + for (Function additionalCheck : additionalChecks) { + IEnumId validationError = additionalCheck.apply(fromMap); + if (validationError != null) + return of(validationError, fieldName); + } + return empty(); + } +} diff --git a/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/TimeNotBeforeRule.java b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/TimeNotBeforeRule.java new file mode 100644 index 000000000..2e76dda1b --- /dev/null +++ b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/rules/TimeNotBeforeRule.java @@ -0,0 +1,61 @@ +package ru.spcex.clearing.validation.common.rules; + +import ru.spcex.clearing.validation.common.context.ImdgValidationContext; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.enumeration.IEnumId; + +import java.time.LocalTime; +import java.util.Optional; +import java.util.function.Function; + +/** + * Проверка поля с LocalTime. Условие проверки: проверяемое время >= текущее время + * @param Класс проверяемого объекта + */ +public record TimeNotBeforeRule( + String fieldName, + Function getter, + IEnumId errorEmptyRequiredValue, + IEnumId errorTaskForPastTime, + boolean required +) implements IValidationRule> { + + /** + * @param fieldName Название поля класса, используется для передачи ошибки + * @param getter Метод получения проверяемого времени + * @param errorEmptyRequiredValue Ошибка, которая будет возвращена, если поле пустое + * @param errorTaskForPastTime Ошибка, которая будет возвращена, если проверяемое время меньше текущего + * @param required Флаг обязательности поля + * @param Класс проверяемого объекта + */ + public static TimeNotBeforeRule instance(String fieldName, + Function getter, + IEnumId errorEmptyRequiredValue, + IEnumId errorTaskForPastTime, + boolean required) { + return new TimeNotBeforeRule<>(fieldName, getter, errorEmptyRequiredValue, errorTaskForPastTime, required); + } + + /** + * @param fieldName Название поля класса, используется для передачи ошибки + * @param getter Метод получения проверяемого времени + * @param errorEmptyRequiredValue Ошибка, которая будет возвращена, если поле пустое + * @param errorTaskForPastTime Ошибка, которая будет возвращена, если проверяемое время меньше текущего + * @param Класс проверяемого объекта + */ + public static TimeNotBeforeRule instance(String fieldName, + Function getter, + IEnumId errorEmptyRequiredValue, + IEnumId errorTaskForPastTime) { + return new TimeNotBeforeRule<>(fieldName, getter, errorEmptyRequiredValue, errorTaskForPastTime, true); + } + + @Override + public Optional validate(ImdgValidationContext context) { + R validatedObject = context.getValidatedObject(); + LocalTime date = getter.apply(validatedObject); + if (date == null) return required ? of(errorEmptyRequiredValue, fieldName) : Optional.empty(); + if (date.isBefore(LocalTime.now())) return of(errorTaskForPastTime, fieldName); + return Optional.empty(); + } +} diff --git a/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/validator/IValidator.java b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/validator/IValidator.java new file mode 100644 index 000000000..c5a9b7322 --- /dev/null +++ b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/validator/IValidator.java @@ -0,0 +1,46 @@ +package ru.spcex.clearing.validation.common.validator; + + +import ru.spcex.clearing.validation.common.rules.IValidationRule; +import ru.spcex.platform.utils.enumeration.EnumMessage; + +import java.util.Collection; +import java.util.Optional; + +/** + * Валидатор, в который можно добавлять правила проверок. + * Будет выполнять их в том порядке, в котором их добавляли + */ +public interface IValidator { + /** + * метод для добавления правил валидации + * если положить правило с некорректными типами, при вызове validate будет ClassCastException + */ + void addRule(IValidationRule rule); + + /** + * если хотим добавить проверки другого валидатора
+ * они будут выполняться только если проверки этого успешно пройдены + * возвращает себя + */ + IValidator chain(IValidator anotherValidator); + + /** + * валидирует все (подходит для случаев когда проверки не полагаются на то что предыдущие проверки прошли нормально) + */ + Collection validateAll(); + + /** + * валидирует до первой ошибки + */ + Optional tillFirstError(); + + /** + * валидирует все, возвращает первую ошибку - для совместимости + */ + Optional validateAllReturnFirst(); + + default S getStored(Enum storedObject) { + throw new UnsupportedOperationException("not implemented"); + } +} diff --git a/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/validator/ValidationHelper.java b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/validator/ValidationHelper.java new file mode 100644 index 000000000..9b75f02f8 --- /dev/null +++ b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/validator/ValidationHelper.java @@ -0,0 +1,36 @@ +package ru.spcex.clearing.validation.common.validator; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.spcex.clearing.platform.messaging.domain.BaseRequest; +import ru.spcex.clearing.platform.messaging.service.RequestInfoUpdate; +import ru.spcex.clearing.platform.messaging.service.Status; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.enumeration.IMessageResolver; + +import java.util.Optional; +import java.util.function.Function; + +public class ValidationHelper { + private final static Logger log = LoggerFactory.getLogger(ValidationHelper.class); + + public static RequestInfoUpdate validate(BaseRequest userRequest, + Function validatorBuilder, + IMessageResolver messageResolver) { + if (validatorBuilder != null) { + R req = userRequest.getRequestPayload(); + IValidator validator = validatorBuilder.apply(req); + Optional validationError = validator.tillFirstError(); + if (validationError.isPresent()) { + String errorMsg = messageResolver.resolve(validationError.get()); + log.error("validation error for {} error={}, id={}: {}", req.getClass().getSimpleName(), validationError.get().getSubject(), userRequest.getId(), errorMsg); + return new RequestInfoUpdate() + .setId(userRequest.getId()) + .setStatus(Status.Error) + .setMessage(errorMsg); + } + } + return null; + } + +} diff --git a/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/validator/ValidatorImpl.java b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/validator/ValidatorImpl.java new file mode 100644 index 000000000..f647dc909 --- /dev/null +++ b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/validator/ValidatorImpl.java @@ -0,0 +1,113 @@ +package ru.spcex.clearing.validation.common.validator; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.spcex.clearing.validation.common.context.IValidatorContext; +import ru.spcex.clearing.validation.common.rules.IValidationRule; +import ru.spcex.platform.utils.enumeration.EnumMessage; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +/** + * Валидатор, предназначенный для правил у которых есть "контекст" валидации + */ +public class ValidatorImpl implements IValidator { + private final Logger log = LoggerFactory.getLogger(getClass()); + private List> rules = new ArrayList<>(); + private C context; + private IValidator chainedValidator; + + public ValidatorImpl(C context) {this.context = context;} + + public ValidatorImpl(C context, IValidationRule... rules) { // + this.context = context; + for (IValidationRule rule : rules) { + addRule(rule); + } + } + + public void setContext(C context) { + this.context = context; + } + + @Override + @SuppressWarnings("unchecked") + public void addRule(IValidationRule rule) { + rules.add((IValidationRule) rule); + } + + @Override + public IValidator chain(IValidator anotherValidator) { + if (chainedValidator == null) { + chainedValidator = anotherValidator; + } else { + chainedValidator.chain(anotherValidator); + } + return this; + } + + @Override + public Collection validateAll() { + List errors = new ArrayList<>(); + Instant begin = Instant.now(); + for (IValidationRule rule : rules) { + if (rule.runIfErrorsPresent() || errors.size() == 0) { + Optional validationError; + validationError = rule.validate(context); + validationError.ifPresent(errors::add); + if (validationError.isPresent()) { + log.debug("validating stage: {} error", rule.ruleName()); + } else { + log.debug("validating stage: {} complete", rule.ruleName()); + } + } + } + log.debug("validation {} in {} ms", + errors.size() == 0 ? "successfully completed" : "completed with errors", + Instant.now().toEpochMilli() - begin.toEpochMilli()); + if (chainedValidator != null && errors.size() == 0) errors.addAll(chainedValidator.validateAll()); + return errors; + } + + @Override + public Optional tillFirstError() { + Instant begin = Instant.now(); + for (IValidationRule rule : rules) { + log.debug("validating stage: {}", rule.ruleName()); + Optional validationError; + validationError = rule.validate(context); + if (validationError.isPresent()) { + log.debug("validation stage: {} completed with error {} in {} ms", + rule.ruleName(), validationError.get().getSubject().getId(), + Instant.now().toEpochMilli() - begin.toEpochMilli()); + return validationError; + } + } + log.debug("validation successfully completed in {} ms", Instant.now().toEpochMilli() - begin.toEpochMilli()); + if (chainedValidator != null) return chainedValidator.tillFirstError(); + return Optional.empty(); + } + + @Override + public Optional validateAllReturnFirst() { + Collection allErrors = validateAll(); + if (allErrors.size() > 0) { + EnumMessage error = allErrors.iterator().next(); + return Optional.of(error); + } else + return Optional.empty(); + } + + @Override + public S getStored(Enum storedObject) { + return context.getStoredObject(storedObject); + } + + public C getContext() { + return context; + } +} diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/ClearingMemberCategoryValidationConfig.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/ClearingMemberCategoryValidationConfig.java new file mode 100644 index 000000000..f338757e8 --- /dev/null +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/ClearingMemberCategoryValidationConfig.java @@ -0,0 +1,114 @@ +//package ru.spcex.clearing.company.config.validation; +// +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import ru.clearing.classes.statics.data.company.Company; +//import ru.clearing.classes.statics.data.generated.ClearingMemberCategory; +//import ru.clearing.platform.dictionary.ClearingCategoryDictionary; +//import ru.spcex.clearing.company.error.CompanyErrors; +//import ru.spcex.clearing.imdg.IMDGDistributedNames; +//import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonDeleteRequest; +//import ru.spcex.clearing.platform.messaging.domain.cud.company.ClearingMemberCategoryNewRequest; +//import ru.spcex.clearing.platform.messaging.domain.cud.company.ClearingMemberCategoryUpdateRequest; +//import ru.spcex.clearing.validation.common.context.ImdgValidationContext; +//import ru.spcex.clearing.validation.common.rules.DictionaryPresentRule; +//import ru.spcex.clearing.validation.common.rules.EnumPresentRule; +//import ru.spcex.clearing.validation.common.rules.FieldRequiredRule; +//import ru.spcex.clearing.validation.common.rules.IdPresentRule; +//import ru.spcex.clearing.validation.common.validator.IValidator; +//import ru.spcex.clearing.validation.common.validator.ValidatorImpl; +//import ru.spcex.platform.classes.base.SpcexObjectBase; +//import ru.spcex.platform.enumeration.ClearingCategory; +//import ru.spcex.platform.enumeration.WorkflowStatus; +//import ru.spcex.platform.imdg.api.Imdg; +// +//import java.util.Map; +//import java.util.function.Consumer; +//import java.util.function.Function; +// +//@Configuration +//public class ClearingMemberCategoryValidationConfig { +// +// @Bean("clearingMemberCategoryNewRequestValidator") +// public Function clearingMemberCategoryNewRequestValidator( +// Map> imdgForValidation +// ) { +// return clearingMemberCategoryNewRequest -> { +// ImdgValidationContext context = new ImdgValidationContext<>(); +// context.setValidatedObject(clearingMemberCategoryNewRequest); +// Consumer addImdg = (s) -> context.addImdg(s, imdgForValidation.get(s)); +// addImdg.accept(IMDGDistributedNames.Map_Company); +// return new ValidatorImpl<>(context, +// IdPresentRule.instance("companyId", +// ClearingMemberCategoryNewRequest::getCompanyId, +// IMDGDistributedNames.Map_Company, +// Company.class, +// CompanyErrors.RequiredFieldEmpty, +// CompanyErrors.CompanyNotFound, +// company -> WorkflowStatus.Active.getKey().equals(company.getWorkflowStatus()) ? null : CompanyErrors.CompanyDisabled), +// FieldRequiredRule.instance("clearingMemberCategory", +// ClearingMemberCategoryNewRequest::getClearingMemberCategory, +// CompanyErrors.RequiredFieldEmpty)); +// }; +// } +// +// @Bean("clearingMemberCategoryUpdateRequestValidator") +// public Function clearingMemberCategoryUpdateRequestValidator( +// Map> imdgForValidation +// ) { +// return clearingMemberCategoryUpdateRequest -> { +// ImdgValidationContext context = new ImdgValidationContext<>(); +// context.setValidatedObject(clearingMemberCategoryUpdateRequest); +// Consumer addImdg = (s) -> context.addImdg(s, imdgForValidation.get(s)); +// addImdg.accept(IMDGDistributedNames.Map_Company); +// addImdg.accept(IMDGDistributedNames.Map_ClearingMemberCategory); +// return new ValidatorImpl<>(context, +// IdPresentRule.instance("id", +// ClearingMemberCategoryUpdateRequest::getId, +// IMDGDistributedNames.Map_ClearingMemberCategory, +// ClearingMemberCategory.class, +// CompanyErrors.RequiredFieldEmpty, +// CompanyErrors.CategoryNotFound, +// clearingMemberCategory -> { +// Imdg companyImdg = context.obtainMap(IMDGDistributedNames.Map_Company, Company.class); +// Company company = companyImdg.getSingleObjectByID(clearingMemberCategory.getCompanyId()); +// if (company == null) return CompanyErrors.CompanyNotFound; +// if (!WorkflowStatus.Active.getKey().equals(company.getWorkflowStatus())) +// return CompanyErrors.CompanyDisabled; +// return null; +// }), +// EnumPresentRule.instance("clearingMemberCategory", +// ClearingMemberCategoryUpdateRequest::getClearingMemberCategory, +// ClearingCategory.values(), +// CompanyErrors.WrongFieldValue, +// CompanyErrors.RequiredFieldEmpty), +// DictionaryPresentRule.instance("clearingMemberCategory", +// ClearingMemberCategoryUpdateRequest::getClearingMemberCategory, +// IMDGDistributedNames.Map_ClearingCategoryDictionary, +// ClearingCategoryDictionary.class, +// CompanyErrors.RequiredFieldEmpty, +// CompanyErrors.WrongFieldValue)); +// }; +// } +// +// @Bean("clearingMemberCategoryDeleteRequestValidator") +// public Function clearingMemberCategoryDeleteRequestValidator( +// Map> imdgForValidation +// ) { +// return clearingMemberCategoryDeleteRequest -> { +// ImdgValidationContext context = new ImdgValidationContext<>(); +// context.setValidatedObject(clearingMemberCategoryDeleteRequest); +// Consumer addImdg = (s) -> context.addImdg(s, imdgForValidation.get(s)); +// addImdg.accept(IMDGDistributedNames.Map_ClearingMemberCategory); +// return new ValidatorImpl<>(context, +// IdPresentRule.instance("id", +// CommonDeleteRequest::getId, +// IMDGDistributedNames.Map_ClearingMemberCategory, +// ClearingMemberCategory.class, +// CompanyErrors.RequiredFieldEmpty, +// CompanyErrors.CategoryNotFound) +// ); +// }; +// } +// +//} diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/ValidationConfig.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/ValidationConfig.java new file mode 100644 index 000000000..e39ce71e4 --- /dev/null +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/ValidationConfig.java @@ -0,0 +1,30 @@ +//package ru.spcex.clearing.company.config.validation; +// +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import ru.clearing.classes.statics.data.company.Company; +//import ru.clearing.classes.statics.data.generated.ClearingMemberCategory; +//import ru.spcex.clearing.imdg.IMDGDistributedNames; +//import ru.spcex.platform.classes.base.SpcexObjectBase; +//import ru.spcex.platform.imdg.api.Imdg; +//import ru.spcex.platform.imdg.api.ImdgProvider; +// +//import java.util.HashMap; +//import java.util.Map; +//import java.util.function.BiConsumer; +// +//@Configuration +//public class ValidationConfig { +// +// @Bean("imdgForValidation") +// public Map> imdgForValidation(ImdgProvider imdgProvider) { +// final Map> imdg = new HashMap<>(); +// BiConsumer> addImdg = (s, aClass) -> imdg.put(s, imdgProvider.getImdg(s, aClass)); +// +// addImdg.accept(IMDGDistributedNames.Map_Company, Company.class); +// addImdg.accept(IMDGDistributedNames.Map_ClearingMemberCategory, ClearingMemberCategory.class); +// +// return imdg; +// } +// +//} diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/error/CompanyErrors.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/error/CompanyErrors.java index 1784b0f5c..ca3edbd16 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/error/CompanyErrors.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/error/CompanyErrors.java @@ -8,7 +8,7 @@ public enum CompanyErrors implements IEnumId { RequiredFieldEmpty(3002L), // Не заданы обязательные поля %s. DictionaryNotFound(3003L), // Запись с идентификатором %s в справочнике %s не найдена. - VrongFieldValue(3004L), // Неверное значение поля %s. + WrongFieldValue(3004L), // Неверное значение поля %s. RecordAlreadyExist(3005L), // Запись с идентификатором %s уже существует. RecordNotFound(3006L), // Запись с идентификатором %s не найдена. CompanyALreadyExist(3010L), // Компания %s уже существует diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/ClearingMemberCategoryService.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/ClearingMemberCategoryService.java index 869049fe8..6ca00b196 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/ClearingMemberCategoryService.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/ClearingMemberCategoryService.java @@ -15,19 +15,40 @@ import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonDeleteReques import ru.spcex.clearing.platform.messaging.domain.cud.company.ClearingMemberCategoryNewRequest; import ru.spcex.clearing.platform.messaging.domain.cud.company.ClearingMemberCategoryUpdateRequest; import ru.spcex.clearing.platform.messaging.service.QueueConsumer; +import ru.spcex.clearing.platform.messaging.service.RequestInfoUpdate; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgProvider; +import ru.spcex.platform.utils.enumeration.IMessageResolver; @Service public class ClearingMemberCategoryService extends QueueConsumer implements InitializingBean { private final Logger log = LoggerFactory.getLogger(getClass()); private final Imdg clearingMemberCategoryMap; + private final IMessageResolver messageResolver; + + /* + private final Function clearingMemberCategoryNewRequestValidator; + private final Function clearingMemberCategoryUpdateRequestValidator; + private final Function clearingMemberCategoryDeleteRequestValidator; + */ + @Autowired - public ClearingMemberCategoryService(Consumer kafkaQueue, Producer kafkaProducer, - ImdgProvider imdgProvider) { + public ClearingMemberCategoryService(Consumer kafkaQueue, + Producer kafkaProducer, + ImdgProvider imdgProvider, + IMessageResolver messageResolver /*, + @Qualifier("clearingMemberCategoryNewRequestValidator") Function clearingMemberCategoryNewRequestValidator, + @Qualifier("clearingMemberCategoryUpdateRequestValidator") Function clearingMemberCategoryUpdateRequestValidator, + @Qualifier("clearingMemberCategoryDeleteRequestValidator") Function clearingMemberCategoryDeleteRequestValidator */) { super(kafkaQueue, kafkaProducer); this.clearingMemberCategoryMap = imdgProvider.getImdg(IMDGDistributedNames.Map_ClearingMemberCategory, ClearingMemberCategory.class); + this.messageResolver = messageResolver; + /* + this.clearingMemberCategoryNewRequestValidator = clearingMemberCategoryNewRequestValidator; + this.clearingMemberCategoryUpdateRequestValidator = clearingMemberCategoryUpdateRequestValidator; + this.clearingMemberCategoryDeleteRequestValidator = clearingMemberCategoryDeleteRequestValidator; + */ } @Override @@ -44,19 +65,29 @@ public class ClearingMemberCategoryService extends QueueConsumer implements Init init(); } - private void clearingMemberCategoryNew(BaseRequest userRequest) { + private RequestInfoUpdate clearingMemberCategoryNew(BaseRequest userRequest) { ClearingMemberCategoryNewRequest req = userRequest.getRequestPayload(); + +// RequestInfoUpdate requestInfoUpdate = ValidationHelper.validate(userRequest, clearingMemberCategoryNewRequestValidator, messageResolver); +// if (requestInfoUpdate != null) return requestInfoUpdate; + log.debug("ClearingMemberCategoryUpdateRequest received {}", userRequest.getId()); ClearingMemberCategory newCategory = new ClearingMemberCategory(); newCategory.setCompanyId(req.getCompanyId()); newCategory.setClearingMemberCategory(req.getClearingMemberCategory()); clearingMemberCategoryMap.insert(newCategory); log.debug("successfully processed, new category id {}", newCategory.getId()); + + return null; } - public void clearingMemberCategoryUpdate(BaseRequest userRequest) { + public RequestInfoUpdate clearingMemberCategoryUpdate(BaseRequest userRequest) { ClearingMemberCategoryUpdateRequest req = userRequest.getRequestPayload(); + +// RequestInfoUpdate requestInfoUpdate = ValidationHelper.validate(userRequest, clearingMemberCategoryUpdateRequestValidator, messageResolver); +// if (requestInfoUpdate != null) return requestInfoUpdate; + log.debug("ClearingMemberCategoryUpdateRequest received"); ClearingMemberCategory clearingMemberCategory = clearingMemberCategoryMap.getSingleObjectByID(req.getId()); @@ -64,12 +95,19 @@ public class ClearingMemberCategoryService extends QueueConsumer implements Init clearingMemberCategoryMap.update(clearingMemberCategory); log.debug("successfully processed, id {}", clearingMemberCategory.getId()); + return null; } - private void clearingMemberCategoryDelete(BaseRequest userRequest) { + private RequestInfoUpdate clearingMemberCategoryDelete(BaseRequest userRequest) { CommonDeleteRequest req = userRequest.getRequestPayload(); + +// RequestInfoUpdate requestInfoUpdate = ValidationHelper.validate(userRequest, clearingMemberCategoryDeleteRequestValidator, messageResolver); +// if (requestInfoUpdate != null) return requestInfoUpdate; + log.debug("CommonDeleteRequest received id = {}", req.getId()); ClearingMemberCategory clearingMemberCategory = clearingMemberCategoryMap.getSingleObjectByID(req.getId()); clearingMemberCategoryMap.delete(clearingMemberCategory); + + return null; } } diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/common/CommonDeleteRequest.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/common/CommonDeleteRequest.java index 5ad1de612..320c4d796 100644 --- a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/common/CommonDeleteRequest.java +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/common/CommonDeleteRequest.java @@ -7,6 +7,7 @@ public class CommonDeleteRequest implements WithId { @JsonProperty public Long id; + @Override public Long getId() { return id; } diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/service/QueueConsumer.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/service/QueueConsumer.java index ad10ca627..706b50f36 100644 --- a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/service/QueueConsumer.java +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/service/QueueConsumer.java @@ -171,6 +171,7 @@ public class QueueConsumer implements AutoCloseable { * @param Класс проверяемого запроса * @return null если ошибок нет */ + @Deprecated public RequestInfoUpdate validate(BaseRequest userRequest, Function validatorBuilder, IMessageResolver messageResolver) { diff --git a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/validation/IValidationRule.java b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/validation/IValidationRule.java index 3ad88a3f1..707751089 100644 --- a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/validation/IValidationRule.java +++ b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/validation/IValidationRule.java @@ -10,6 +10,7 @@ import java.util.Optional; * Все необходимые данные берет из контекста * @param контекст */ +// todo move this to clearing-utils (or move clearing-utils public interface IValidationRule { Optional validate(C context);