From f4af098a6fa54939cae57963e637fbdd65342e4d Mon Sep 17 00:00:00 2001 From: anhefti Date: Tue, 30 Apr 2019 21:39:59 +0200 Subject: [PATCH] SEBSERV-44 SEBSERV-45 validation on back end --- .../examconfig/impl/TextFieldBuilder.java | 57 ++++------------- .../dao/impl/ConfigurationValueDAOImpl.java | 57 ++++++++--------- .../ConfigurationValueValidator.java | 41 +++++++++++++ .../sebconfig/SebExamConfigService.java | 20 ++++++ .../impl/IntegerTypeValueValidator.java | 55 +++++++++++++++++ .../impl/SebExamConfigServiceImpl.java | 61 +++++++++++++++++++ .../api/ConfigurationValueController.java | 26 +++++--- 7 files changed, 230 insertions(+), 87 deletions(-) create mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ConfigurationValueValidator.java create mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebExamConfigService.java create mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/IntegerTypeValueValidator.java create mode 100644 src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebExamConfigServiceImpl.java diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TextFieldBuilder.java b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TextFieldBuilder.java index c9e927d5..27d2f57f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TextFieldBuilder.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/service/examconfig/impl/TextFieldBuilder.java @@ -9,7 +9,6 @@ package ch.ethz.seb.sebserver.gui.service.examconfig.impl; import java.util.Collection; -import java.util.function.Consumer; import org.eclipse.rap.rwt.RWT; import org.eclipse.swt.SWT; @@ -85,59 +84,26 @@ public class TextFieldBuilder implements InputFieldBuilder { errorLabel.setVisible(false); errorLabel.setData(RWT.CUSTOM_VARIANT, "error"); - addValueChangeListener( - text, - attribute, - orientation, - viewContext); - - return new TextInputField(attribute, orientation, text, errorLabel); - } - - private void addValueChangeListener( - final Text control, - final ConfigurationAttribute attribute, - final Orientation orientation, - final ViewContext viewContext) { - + final TextInputField textInputField = new TextInputField(attribute, orientation, text, errorLabel); final ValueChangeListener valueListener = viewContext.getValueChangeListener(); - if (attribute.type == AttributeType.INTEGER) { - addNumberCheckListener(control, attribute, s -> Integer.parseInt(s), viewContext); - } else if (attribute.type == AttributeType.DECIMAL) { - addNumberCheckListener(control, attribute, s -> Double.parseDouble(s), viewContext); - } else { - control.addListener( - SWT.FocusOut, - event -> valueListener.valueChanged( + text.addListener( + SWT.FocusOut, + event -> { + textInputField.clearError(); + valueListener.valueChanged( viewContext, attribute, - String.valueOf(control.getText()), - 0)); - } - } + String.valueOf(text.getText()), + textInputField.listIndex); + }); - private void addNumberCheckListener( - final Text control, - final ConfigurationAttribute attribute, - final Consumer numberCheck, - final ViewContext viewContext) { - - final ValueChangeListener valueListener = viewContext.getValueChangeListener(); - control.addListener(SWT.FocusOut, event -> { - try { - final String text = control.getText(); - numberCheck.accept(text); - viewContext.clearError(attribute.id); - valueListener.valueChanged(viewContext, attribute, text, 0); - } catch (final NumberFormatException e) { - viewContext.showError(attribute.id, "Not A Number"); - } - }); + return textInputField; } static final class TextInputField extends ControlFieldAdapter { private String initValue = ""; + private int listIndex = 0; TextInputField( final ConfigurationAttribute attribute, @@ -155,6 +121,7 @@ public class TextFieldBuilder implements InputFieldBuilder { .findFirst() .map(v -> { this.initValue = v.value; + this.listIndex = (v.listIndex != null) ? v.listIndex : 0; setDefaultValue(); return this.initValue; }); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationValueDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationValueDAOImpl.java index 1a3ae3af..2d47f0de 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationValueDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationValueDAOImpl.java @@ -33,6 +33,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue; import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.gbl.util.Utils; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationAttributeRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationAttributeRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationRecordMapper; @@ -156,9 +157,29 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { .flatMap(this::attributeRecord) .map(attributeRecord -> { + final Long id; + if (data.id == null) { + id = this.configurationValueRecordMapper.selectIdsByExample() + .where( + ConfigurationValueRecordDynamicSqlSupport.configurationId, + isEqualTo(data.configurationId)) + .and( + ConfigurationValueRecordDynamicSqlSupport.configurationAttributeId, + isEqualTo(data.attributeId)) + .and( + ConfigurationValueRecordDynamicSqlSupport.listIndex, + isEqualTo(data.listIndex)) + .build() + .execute() + .stream() + .collect(Utils.toSingleton()); + } else { + id = data.id; + } + final boolean bigValue = isBigValue(attributeRecord); final ConfigurationValueRecord newRecord = new ConfigurationValueRecord( - data.id, + id, null, null, null, @@ -166,13 +187,8 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { (bigValue) ? null : data.value, (bigValue) ? data.value : null); - if (data.id != null) { - this.configurationValueRecordMapper.updateByPrimaryKeySelective(newRecord); - } else { - saveByMatch(data, newRecord); - } - return this.configurationValueRecordMapper.selectByPrimaryKey(data.id); - + this.configurationValueRecordMapper.updateByPrimaryKeySelective(newRecord); + return this.configurationValueRecordMapper.selectByPrimaryKey(id); }) .flatMap(ConfigurationValueDAOImpl::toDomainModel) .onError(TransactionHandler::rollback); @@ -424,29 +440,4 @@ public class ConfigurationValueDAOImpl implements ConfigurationValueDAO { return data; } - /** Try to identify and save attribute value by configurationId and configurationAttributeId and listIndex - * - * @param data - * @param newRecord - * @throws ResourceNotFoundException if no matching attribute value was found */ - private void saveByMatch(final ConfigurationValue data, final ConfigurationValueRecord newRecord) { - - final Integer execute = this.configurationValueRecordMapper.updateByExample(newRecord) - .where( - ConfigurationValueRecordDynamicSqlSupport.configurationId, - isEqualTo(data.configurationId)) - .and( - ConfigurationValueRecordDynamicSqlSupport.configurationAttributeId, - isEqualTo(data.attributeId)) - .and( - ConfigurationValueRecordDynamicSqlSupport.listIndex, - isEqualTo(data.listIndex)) - .build() - .execute(); - - if (execute == null || execute < 0) { - throw new ResourceNotFoundException(EntityType.CONFIGURATION_VALUE, data.toString()); - } - } - } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ConfigurationValueValidator.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ConfigurationValueValidator.java new file mode 100644 index 00000000..978b1dcf --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/ConfigurationValueValidator.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig; + +import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; + +public interface ConfigurationValueValidator { + + public static final String MESSAGE_VALUE_OBJECT_NAME = "examConfigValue"; + + String name(); + + boolean validate( + ConfigurationValue value, + ConfigurationAttribute attribute); + + default void throwValidationError( + final ConfigurationValue value, + final ConfigurationAttribute attribute) { + + throw new FieldValidationException( + attribute.name, + this.createErrorMessage(value, attribute)); + } + + default String createErrorMessage( + final ConfigurationValue value, + final ConfigurationAttribute attribute) { + + return "examConfigValue:" + attribute.name + ":" + name() + ":" + value.listIndex; + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebExamConfigService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebExamConfigService.java new file mode 100644 index 00000000..b90d452e --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/SebExamConfigService.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig; + +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; + +public interface SebExamConfigService { + + void validate(ConfigurationValue value); + + void validate(ConfigurationTableValue tableValue); + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/IntegerTypeValueValidator.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/IntegerTypeValueValidator.java new file mode 100644 index 00000000..97b9dd50 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/IntegerTypeValueValidator.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; +import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationValueValidator; + +@Lazy +@Component +@WebServiceProfile +public class IntegerTypeValueValidator implements ConfigurationValueValidator { + + @Override + public String name() { + return AttributeType.INTEGER.name(); + } + + @Override + public boolean validate( + final ConfigurationValue value, + final ConfigurationAttribute attribute) { + + // if value is not an integer type or another specific validation is defined --> skip + if (attribute.type != AttributeType.INTEGER || + StringUtils.isNoneBlank(attribute.validator)) { + + return true; + } + + if (StringUtils.isBlank(value.value)) { + return true; + } + + try { + Integer.parseInt(value.value); + return true; + } catch (final NumberFormatException nfe) { + return false; + } + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebExamConfigServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebExamConfigServiceImpl.java new file mode 100644 index 00000000..fe7049c0 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/sebconfig/impl/SebExamConfigServiceImpl.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl; + +import java.util.Collection; +import java.util.Objects; + +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationTableValue; +import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue; +import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationAttributeDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConfigurationValueValidator; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebExamConfigService; + +@Lazy +@Service +@WebServiceProfile +public class SebExamConfigServiceImpl implements SebExamConfigService { + + private final ConfigurationAttributeDAO configurationAttributeDAO; + private final Collection validators; + + protected SebExamConfigServiceImpl( + final ConfigurationAttributeDAO configurationAttributeDAO, + final Collection validators) { + + this.configurationAttributeDAO = configurationAttributeDAO; + this.validators = validators; + } + + @Override + public void validate(final ConfigurationValue value) { + Objects.requireNonNull(value); + + final ConfigurationAttribute attribute = this.configurationAttributeDAO.byPK(value.attributeId) + .getOrThrow(); + + this.validators + .stream() + .filter(validator -> !validator.validate(value, attribute)) + .findFirst() + .ifPresent(validator -> validator.throwValidationError(value, attribute)); + } + + @Override + public void validate(final ConfigurationTableValue tableValue) { + // TODO Auto-generated method stub + + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationValueController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationValueController.java index a13bea3a..9e713b55 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationValueController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationValueController.java @@ -35,6 +35,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionServic import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ConfigurationValueDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.SebExamConfigService; import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService; @WebServiceProfile @@ -44,6 +45,7 @@ public class ConfigurationValueController extends EntityController