backend-api http://jira.mfd.msk:8088/browse/CLS-331 валидация запросов (action) по meta.json (обязательность полей)/
This commit is contained in:
parent
58b396c849
commit
14f7a814aa
6 changed files with 200 additions and 4 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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<String, Object> kafka;
|
||||
private final ImdgId idGenerator;
|
||||
private final ImdgProvider imdgProvider;
|
||||
private final ActionMetaValidation actionMetaValidation;
|
||||
private final ActionValidationProvider validation;
|
||||
private final Imdg<User> userImdg;
|
||||
|
||||
public OperatorImpl(Producer<String, Object> kafka, ImdgProvider imdgProvider, ActionValidationProvider validation) {
|
||||
public OperatorImpl(Producer<String, Object> 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) {
|
||||
|
|
|
|||
|
|
@ -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<String, Function<Object, IValidator>> validators;
|
||||
protected final MetaServer meta;
|
||||
|
||||
@Autowired
|
||||
public ActionMetaValidation(MetaServer meta) {
|
||||
this.validators = new HashMap<>();
|
||||
this.meta = meta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
for (Map.Entry<String, ObjectExtracted> entry : meta.getActionObjectsExtracted().entrySet()) {
|
||||
if (StringUtils.isEmpty(entry.getKey())) continue;
|
||||
Function<Object, IValidator> 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<Object, IValidator> constructor = (Function<Object, IValidator>) validators.get(actionDestination);
|
||||
if (constructor != null)
|
||||
return constructor.apply(action);
|
||||
return null;
|
||||
}
|
||||
|
||||
private <T extends Object> Function<T, IValidator> 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<T> ctx = new ImdgValidationContext<>();
|
||||
ctx.setValidatedObject(iAcc);
|
||||
ValidatorImpl<ImdgValidationContext<T>> iValidator = new ValidatorImpl(ctx);
|
||||
iValidator.addRule(metaValidatorRule);
|
||||
// в дальнейшем можно улучшить и разделить валидатор по полям
|
||||
return iValidator;
|
||||
};
|
||||
}
|
||||
|
||||
static class MetaValidatorRule implements IValidationRule<ImdgValidationContext<IAction>> {
|
||||
public final ObjectExtracted metaAction;
|
||||
|
||||
public MetaValidatorRule(ObjectExtracted metaAction) {
|
||||
this.metaAction = metaAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<EnumMessage> validate(ImdgValidationContext<IAction> 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<String, Object> kafka, ActionValidationProvider validationProvider) {
|
||||
return new OperatorImpl(kafka, hazelcastServiceTest, validationProvider);
|
||||
public OperatorImpl createIOperator(Producer<String, Object> kafka,
|
||||
ActionMetaValidation actionMetaValidation, ActionValidationProvider validationProvider) {
|
||||
return new OperatorImpl(kafka, hazelcastServiceTest, actionMetaValidation, validationProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
|
|||
|
|
@ -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<EnumMessage> 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<EnumMessage> vResult = v.validateAll();
|
||||
assertEquals("[]", vResult.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue