From 14f7a814aaa010bb4648cae71791c1cfdcea961b Mon Sep 17 00:00:00 2001 From: AKurakin Date: Thu, 25 May 2023 17:24:55 +0300 Subject: [PATCH] =?UTF-8?q?backend-api=20http://jira.mfd.msk:8088/browse/C?= =?UTF-8?q?LS-331=20=D0=B2=D0=B0=D0=BB=D0=B8=D0=B4=D0=B0=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D0=BE=D0=B2=20(action)?= =?UTF-8?q?=20=D0=BF=D0=BE=20meta.json=20(=D0=BE=D0=B1=D1=8F=D0=B7=D0=B0?= =?UTF-8?q?=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D0=B5=D0=B9)/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../clearing/backendapi/meta/MetaServer.java | 4 +- .../backendapi/service/impl/OperatorImpl.java | 18 ++- .../validation/ActionMetaValidation.java | 115 ++++++++++++++++++ .../controller/config/IOperatorTest.java | 6 +- .../validation/ActionMetaValidationTest.java | 59 +++++++++ .../backend-api/src/test/resources/meta.json | 2 + 6 files changed, 200 insertions(+), 4 deletions(-) create mode 100644 clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/validation/ActionMetaValidation.java create mode 100644 clearing-parent/backend-api/src/test/java/ru/spcex/clearing/backendapi/service/validation/ActionMetaValidationTest.java diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/meta/MetaServer.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/meta/MetaServer.java index e345d0911..82557d2ba 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/meta/MetaServer.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/meta/MetaServer.java @@ -82,7 +82,9 @@ public class MetaServer extends MetaBase { log.warn("META SERVER >>> {}", e.getLocalizedMessage()); continue; } - actionObjectsExtracted.put(actionElement.getDestination(), oe); + String actionDestination = // обычно бывают =null о этому эффективнее по имени класса а не по: objectElement.getSubscription().destination + "/"+ actionElement.getDestination(); + oe.getClassName(); + actionObjectsExtracted.put(actionDestination, oe); } } } catch (Throwable e) { diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/impl/OperatorImpl.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/impl/OperatorImpl.java index 3757706ad..009d24485 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/impl/OperatorImpl.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/impl/OperatorImpl.java @@ -17,6 +17,7 @@ import ru.spcex.clearing.backendapi.errors.BackEndError; import ru.spcex.clearing.backendapi.errors.NotFound404Exception; import ru.spcex.clearing.backendapi.security.KeycloakUtils; import ru.spcex.clearing.backendapi.service.IOperator; +import ru.spcex.clearing.backendapi.service.validation.ActionMetaValidation; import ru.spcex.clearing.backendapi.service.validation.ActionValidationProvider; import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.platform.messaging.domain.BaseRequest; @@ -39,13 +40,16 @@ public class OperatorImpl implements IOperator { private final Producer kafka; private final ImdgId idGenerator; private final ImdgProvider imdgProvider; + private final ActionMetaValidation actionMetaValidation; private final ActionValidationProvider validation; private final Imdg userImdg; - public OperatorImpl(Producer kafka, ImdgProvider imdgProvider, ActionValidationProvider validation) { + public OperatorImpl(Producer kafka, ImdgProvider imdgProvider, + ActionMetaValidation actionMetaValidation, ActionValidationProvider validation) { this.kafka = kafka; this.idGenerator = imdgProvider.getImdgIdGenerator(); this.imdgProvider = imdgProvider; + this.actionMetaValidation = actionMetaValidation; this.validation = validation; this.userImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_User, User.class); } @@ -112,6 +116,18 @@ public class OperatorImpl implements IOperator { if (validationErrors.size() > 0) { throw new ActionValidationException(validationErrors); } + // Валидация по мете (рекомендуется использовать её,а не iAction.validate(); + IValidator metaValidator = actionMetaValidation.getValidator(destination, iAction); + if (metaValidator != null) { + metaValidator.tillFirstError().ifPresent(enumMessage -> { + if (enumMessage.getSubject().equalsById(BackEndError.ResourceNotFound.getId())) { + throw new NotFound404Exception(enumMessage); + } else { + throw new ActionValidationException(enumMessage); + } + }); + } + //валидация требующая IMDG поиска других сущностей и т.д. IValidator validator = validation.getValidator(destination, iAction); if (validator != null) { diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/validation/ActionMetaValidation.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/validation/ActionMetaValidation.java new file mode 100644 index 000000000..1b6039679 --- /dev/null +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/validation/ActionMetaValidation.java @@ -0,0 +1,115 @@ +package ru.spcex.clearing.backendapi.service.validation; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import ru.spcex.clearing.backendapi.domain.actions.IAction; +import ru.spcex.clearing.backendapi.errors.BackEndError; +import ru.spcex.clearing.backendapi.meta.FieldExtracted; +import ru.spcex.clearing.backendapi.meta.MetaServer; +import ru.spcex.clearing.backendapi.meta.ObjectExtracted; +import ru.spcex.platform.imdg.validation.ImdgValidationContext; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.validation.IValidationRule; +import ru.spcex.platform.utils.validation.IValidator; +import ru.spcex.platform.utils.validation.ValidatorImpl; + +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +/** + * Проверка обязательности полей для IAction по мете + */ +@Component +public class ActionMetaValidation implements InitializingBean { + protected final Logger log = LoggerFactory.getLogger(getClass()); + private final Map> validators; + protected final MetaServer meta; + + @Autowired + public ActionMetaValidation(MetaServer meta) { + this.validators = new HashMap<>(); + this.meta = meta; + } + + @Override + public void afterPropertiesSet() { + for (Map.Entry entry : meta.getActionObjectsExtracted().entrySet()) { + if (StringUtils.isEmpty(entry.getKey())) continue; + Function validator = validatorConstructor(entry.getValue()); + if (validator == null) continue; + validators.put(entry.getKey(), validator); + } + log.debug("Init {} IAction validator for: {}", validators.size(), validators.keySet()); + } + + @SuppressWarnings("unchecked") + public IValidator getValidator(String destination, Object action) { + if (action == null) + return null; + //if (destination == null) destination = action.getClass().getName(); + String actionDestination = action.getClass().getName(); // см. реализацию в MetaServer String actionDestination =... + Function constructor = (Function) validators.get(actionDestination); + if (constructor != null) + return constructor.apply(action); + return null; + } + + private Function validatorConstructor(ObjectExtracted metaAction) { + if (!metaAction.getFields().stream().anyMatch(field -> field.getField().isRequired())) { + log.debug("Action {}: no any required fields. Do not need validator.", metaAction.getClassName()); + // нет обязательных полей для валидации + return null; + } + final MetaValidatorRule metaValidatorRule = new MetaValidatorRule(metaAction); + return iAcc -> { + ImdgValidationContext ctx = new ImdgValidationContext<>(); + ctx.setValidatedObject(iAcc); + ValidatorImpl> iValidator = new ValidatorImpl(ctx); + iValidator.addRule(metaValidatorRule); + // в дальнейшем можно улучшить и разделить валидатор по полям + return iValidator; + }; + } + + static class MetaValidatorRule implements IValidationRule> { + public final ObjectExtracted metaAction; + + public MetaValidatorRule(ObjectExtracted metaAction) { + this.metaAction = metaAction; + } + + @Override + public Optional validate(ImdgValidationContext context) { + IAction object = context.getValidatedObject(); + try { + for (FieldExtracted field : metaAction.getFields()) { + if (field.getField().isRequired() != null && field.getField().isRequired()) { + Object value = field.extractValue(object); + if (value == null) + return of(BackEndError.ValidationError, field.getMemberName()); +// if (value instanceof String && ((String)value).isEmpty()) // пустое поле, но не null +// return of(BackEndError.ValidationError, field.getMemberName()); + } + } + + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException("Validator error verify " + object + " by " + metaAction); + } + return empty(); + } + + @Override + public String ruleName() { + return getClass().getSimpleName() + " of " + metaField.getClassName(); + } + + } + +} diff --git a/clearing-parent/backend-api/src/test/java/ru/spcex/clearing/backendapi/controller/config/IOperatorTest.java b/clearing-parent/backend-api/src/test/java/ru/spcex/clearing/backendapi/controller/config/IOperatorTest.java index e82944594..3bb4f522d 100644 --- a/clearing-parent/backend-api/src/test/java/ru/spcex/clearing/backendapi/controller/config/IOperatorTest.java +++ b/clearing-parent/backend-api/src/test/java/ru/spcex/clearing/backendapi/controller/config/IOperatorTest.java @@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import ru.spcex.clearing.backendapi.service.impl.OperatorImpl; +import ru.spcex.clearing.backendapi.service.validation.ActionMetaValidation; import ru.spcex.clearing.backendapi.service.validation.ActionValidationProvider; import ru.spcex.platform.imdg.iml.hazelcast.service.HazelcastService; @@ -18,8 +19,9 @@ public class IOperatorTest { @Autowired @Bean("iOperatorTest") - public OperatorImpl createIOperator(Producer kafka, ActionValidationProvider validationProvider) { - return new OperatorImpl(kafka, hazelcastServiceTest, validationProvider); + public OperatorImpl createIOperator(Producer kafka, + ActionMetaValidation actionMetaValidation, ActionValidationProvider validationProvider) { + return new OperatorImpl(kafka, hazelcastServiceTest, actionMetaValidation, validationProvider); } @Bean diff --git a/clearing-parent/backend-api/src/test/java/ru/spcex/clearing/backendapi/service/validation/ActionMetaValidationTest.java b/clearing-parent/backend-api/src/test/java/ru/spcex/clearing/backendapi/service/validation/ActionMetaValidationTest.java new file mode 100644 index 000000000..9f4484cbf --- /dev/null +++ b/clearing-parent/backend-api/src/test/java/ru/spcex/clearing/backendapi/service/validation/ActionMetaValidationTest.java @@ -0,0 +1,59 @@ +package ru.spcex.clearing.backendapi.service.validation; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import ru.spcex.clearing.backendapi.controller.request.cud.common.CommonDeleteAction; +import ru.spcex.clearing.backendapi.controller.request.cud.company.ListingNewAction; +import ru.spcex.clearing.backendapi.domain.actions.IAction; +import ru.spcex.clearing.backendapi.meta.GetResponseFactoryTestConfiguration; +import ru.spcex.clearing.backendapi.meta.MetaServer; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.enumeration.IEnumKey; +import ru.spcex.platform.utils.validation.IValidator; + +import java.util.ArrayList; +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.*; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = GetResponseFactoryTestConfiguration.class) +class ActionMetaValidationTest { + + @Autowired @Qualifier("metaJsonTest") + protected MetaServer meta; + + @Test + void getValidator() { + ActionMetaValidation metaValidator = new ActionMetaValidation(meta); + metaValidator.afterPropertiesSet(); + + { + IAction action = new ListingNewAction(); + assertTrue(action.validate().isEmpty()); + IValidator v = metaValidator.getValidator(null, action); + assertNull(v, "Пока не прописан в мете"); + } + { + IAction action = new CommonDeleteAction(); + assertFalse(action.validate().isEmpty()); // стандартный валидатор - требуется id + + IValidator v = metaValidator.getValidator(null, action); + Collection vResult = v.validateAll(); + assertEquals("[EnumMessage{subject=ValidationError, args: [id]}]", vResult.toString()); + } + { + CommonDeleteAction action = new CommonDeleteAction(); + action.setId(123L); + assertTrue(action.validate().isEmpty()); // стандартный валидатор - требуется id + + IValidator v = metaValidator.getValidator(null, action); + Collection vResult = v.validateAll(); + assertEquals("[]", vResult.toString()); + } + } +} \ No newline at end of file diff --git a/clearing-parent/backend-api/src/test/resources/meta.json b/clearing-parent/backend-api/src/test/resources/meta.json index 84394d492..2d5c5b1e8 100644 --- a/clearing-parent/backend-api/src/test/resources/meta.json +++ b/clearing-parent/backend-api/src/test/resources/meta.json @@ -4060,6 +4060,8 @@ "confirmation": "companyId,account", + "class": "ru.spcex.clearing.backendapi.controller.request.cud.common.CommonDeleteAction", + "fields": [ {"code": "id", "type": 1,"name": "Идентификатор записи","shortname": "ID","link": "account","linkCode": "id","required": true